diff --git a/src/.idea/.idea.port/.idea/indexLayout.xml b/src/.idea/.idea.port/.idea/indexLayout.xml
index 556fba7..ed6ed36 100644
--- a/src/.idea/.idea.port/.idea/indexLayout.xml
+++ b/src/.idea/.idea.port/.idea/indexLayout.xml
@@ -7,4 +7,4 @@
-
\ No newline at end of file
+
diff --git a/src/.idea/.idea.port/.idea/vcs.xml b/src/.idea/.idea.port/.idea/vcs.xml
index 6c0b863..54e4b96 100644
--- a/src/.idea/.idea.port/.idea/vcs.xml
+++ b/src/.idea/.idea.port/.idea/vcs.xml
@@ -3,4 +3,4 @@
-
\ No newline at end of file
+
diff --git a/src/.idea/.idea.src.dir/.idea/indexLayout.xml b/src/.idea/.idea.src.dir/.idea/indexLayout.xml
index 7b08163..67b8dc9 100644
--- a/src/.idea/.idea.src.dir/.idea/indexLayout.xml
+++ b/src/.idea/.idea.src.dir/.idea/indexLayout.xml
@@ -5,4 +5,4 @@
-
\ No newline at end of file
+
diff --git a/src/.idea/.idea.src.dir/.idea/projectSettingsUpdater.xml b/src/.idea/.idea.src.dir/.idea/projectSettingsUpdater.xml
index 4bb9f4d..2d2b171 100644
--- a/src/.idea/.idea.src.dir/.idea/projectSettingsUpdater.xml
+++ b/src/.idea/.idea.src.dir/.idea/projectSettingsUpdater.xml
@@ -3,4 +3,4 @@
-
\ No newline at end of file
+
diff --git a/src/CommandChainDetector.cs b/src/CommandChainDetector.cs
new file mode 100644
index 0000000..a378057
--- /dev/null
+++ b/src/CommandChainDetector.cs
@@ -0,0 +1,52 @@
+namespace port;
+
+///
+/// Detects if the current command is part of a command chain by analyzing command execution context.
+/// This helps determine when to suppress intermediate output like listing current state.
+///
+internal class CommandChainDetector : ICommandChainDetector
+{
+ private readonly Lazy _shouldDisplayOutput;
+
+ public CommandChainDetector()
+ {
+ _shouldDisplayOutput = new Lazy(DetectShouldDisplayOutput);
+ }
+
+ public bool ShouldDisplayOutput() => _shouldDisplayOutput.Value;
+
+ private static bool DetectShouldDisplayOutput()
+ {
+ try
+ {
+ // Get the current command being executed
+ var args = Environment.GetCommandLineArgs();
+ if (args.Length < 2)
+ return true;
+
+ var command = args[1].ToLowerInvariant();
+
+ // Commands that typically end chains and should show output by default
+ var finalCommands = new[] { "prune", "pr", "list", "ls" };
+
+ // Commands that are typically intermediate in chains
+ var intermediateCommands = new[] { "pull", "p", "run", "r" };
+
+ // Simple heuristic: if this is a command that typically ends chains, show output
+ // Otherwise, for intermediate commands, we suppress by default (conservative approach)
+ if (finalCommands.Contains(command))
+ return true;
+
+ if (intermediateCommands.Contains(command))
+ return false;
+
+ // For other commands (commit, reset, stop, remove, etc.), show output by default
+ return true;
+ }
+ catch
+ {
+ // If we can't determine the chain status, default to showing output
+ return true;
+ }
+ }
+}
diff --git a/src/Commands/Commit/CommitCliCommand.cs b/src/Commands/Commit/CommitCliCommand.cs
index bb381b6..cc66c60 100644
--- a/src/Commands/Commit/CommitCliCommand.cs
+++ b/src/Commands/Commit/CommitCliCommand.cs
@@ -14,83 +14,108 @@ internal class CommitCliCommand(
IGetDigestsByIdQuery getDigestsByIdQuery,
IGetContainersQuery getContainersQuery,
IStopAndRemoveContainerCommand stopAndRemoveContainerCommand,
- ListCliCommand listCliCommand)
- : AsyncCommand
+ ConditionalListCliCommand conditionalListCliCommand
+) : AsyncCommand
{
public override async Task ExecuteAsync(CommandContext context, CommitSettings settings)
{
- var container = await GetContainerAsync(settings) ??
- throw new InvalidOperationException("No running container found");
+ var container =
+ await GetContainerAsync(settings)
+ ?? throw new InvalidOperationException("No running container found");
- await Spinner.StartAsync("Committing container", async ctx =>
- {
- string newTag;
- string imageName;
- string tagPrefix;
- if (settings.Overwrite)
- {
- newTag = container.ImageTag ??
- throw new InvalidOperationException(
- "When using --overwrite, container must have an image tag");
- imageName = container.ImageIdentifier;
- tagPrefix = container.TagPrefix;
- }
- else
+ await Spinner.StartAsync(
+ "Committing container",
+ async ctx =>
{
- var tag = settings.Tag ?? $"{DateTime.Now:yyyyMMddhhmmss}";
- (imageName, tagPrefix, newTag) = await GetNewTagAsync(container, tag);
- }
-
-
- ctx.Status = $"Looking for existing container named '{container.ContainerName}'";
- var containerWithSameTag = await getContainersQuery
- .QueryByContainerIdentifierAndTagAsync(container.ContainerIdentifier, newTag)
- .ToListAsync();
-
- ctx.Status = $"Creating image from running container '{container.ContainerName}'";
- newTag = await createImageFromContainerCommand.ExecuteAsync(container, imageName, tagPrefix, newTag);
-
- ctx.Status = $"Removing containers named '{container.ContainerName}'";
- await Task.WhenAll(containerWithSameTag.Select(async container1 =>
- await stopAndRemoveContainerCommand.ExecuteAsync(container1.Id)));
-
- if (settings.Overwrite)
- {
- if (newTag == null)
- throw new InvalidOperationException("newTag is null");
-
- if (container.ImageTag == null)
- throw new InvalidOperationException(
- "Switch argument not supported when creating image from untagged container");
-
- ctx.Status = "Launching new image";
- var id = await createContainerCommand.ExecuteAsync(container, tagPrefix, newTag);
- await runContainerCommand.ExecuteAsync(id);
- }
- else if (settings.Switch)
- {
- if (newTag == null)
- throw new InvalidOperationException("newTag is null");
-
- if (container.ImageTag == null)
- throw new InvalidOperationException(
- "Switch argument not supported when creating image from untagged container");
-
- ctx.Status = $"Stopping running container '{container.ContainerName}'";
- await stopContainerCommand.ExecuteAsync(container.Id);
-
- ctx.Status = "Launching new image";
- var id = await createContainerCommand.ExecuteAsync(container, tagPrefix, newTag);
- await runContainerCommand.ExecuteAsync(id);
+ string newTag;
+ string imageName;
+ string tagPrefix;
+ if (settings.Overwrite)
+ {
+ newTag =
+ container.ImageTag
+ ?? throw new InvalidOperationException(
+ "When using --overwrite, container must have an image tag"
+ );
+ imageName = container.ImageIdentifier;
+ tagPrefix = container.TagPrefix;
+ }
+ else
+ {
+ var tag = settings.Tag ?? $"{DateTime.Now:yyyyMMddhhmmss}";
+ (imageName, tagPrefix, newTag) = await GetNewTagAsync(container, tag);
+ }
+
+ ctx.Status = $"Looking for existing container named '{container.ContainerName}'";
+ var containerWithSameTag = await getContainersQuery
+ .QueryByContainerIdentifierAndTagAsync(container.ContainerIdentifier, newTag)
+ .ToListAsync();
+
+ ctx.Status = $"Creating image from running container '{container.ContainerName}'";
+ newTag = await createImageFromContainerCommand.ExecuteAsync(
+ container,
+ imageName,
+ tagPrefix,
+ newTag
+ );
+
+ ctx.Status = $"Removing containers named '{container.ContainerName}'";
+ await Task.WhenAll(
+ containerWithSameTag.Select(async container1 =>
+ await stopAndRemoveContainerCommand.ExecuteAsync(container1.Id)
+ )
+ );
+
+ if (settings.Overwrite)
+ {
+ if (newTag == null)
+ throw new InvalidOperationException("newTag is null");
+
+ if (container.ImageTag == null)
+ throw new InvalidOperationException(
+ "Switch argument not supported when creating image from untagged container"
+ );
+
+ ctx.Status = "Launching new image";
+ var id = await createContainerCommand.ExecuteAsync(
+ container,
+ tagPrefix,
+ newTag
+ );
+ await runContainerCommand.ExecuteAsync(id);
+ }
+ else if (settings.Switch)
+ {
+ if (newTag == null)
+ throw new InvalidOperationException("newTag is null");
+
+ if (container.ImageTag == null)
+ throw new InvalidOperationException(
+ "Switch argument not supported when creating image from untagged container"
+ );
+
+ ctx.Status = $"Stopping running container '{container.ContainerName}'";
+ await stopContainerCommand.ExecuteAsync(container.Id);
+
+ ctx.Status = "Launching new image";
+ var id = await createContainerCommand.ExecuteAsync(
+ container,
+ tagPrefix,
+ newTag
+ );
+ await runContainerCommand.ExecuteAsync(id);
+ }
}
- });
+ );
- await listCliCommand.ExecuteAsync();
+ await conditionalListCliCommand.ExecuteAsync();
return 0;
}
- private async Task<(string imageName, string tagPrefix, string newTag)> GetNewTagAsync(Container container,
- string tag)
+ private async Task<(string imageName, string tagPrefix, string newTag)> GetNewTagAsync(
+ Container container,
+ string tag
+ )
{
var image = await getImageQuery.QueryAsync(container.ImageIdentifier, container.ImageTag);
string imageName;
@@ -101,7 +126,8 @@ await Task.WhenAll(containerWithSameTag.Select(async container1 =>
var digest = digests?.SingleOrDefault();
if (digest == null || !DigestHelper.TryGetImageNameAndId(digest, out var nameNameAndId))
throw new InvalidOperationException(
- $"Unable to determine image name from running container '{container.ContainerName}'");
+ $"Unable to determine image name from running container '{container.ContainerName}'"
+ );
imageName = nameNameAndId.imageName;
}
else
@@ -114,7 +140,8 @@ await Task.WhenAll(containerWithSameTag.Select(async container1 =>
var tagPrefix = container.TagPrefix;
var newTag = baseTag == null ? tag : $"{tagPrefix}{baseTag}-{tag}";
- if (newTag.Contains('.')) throw new ArgumentException("only [a-zA-Z0-9][a-zA-Z0-9_-] are allowed");
+ if (newTag.Contains('.'))
+ throw new ArgumentException("only [a-zA-Z0-9][a-zA-Z0-9_-] are allowed");
return (imageName, tagPrefix, newTag);
}
@@ -129,4 +156,4 @@ await Task.WhenAll(containerWithSameTag.Select(async container1 =>
var identifier = containerNamePrompt.GetIdentifierOfContainerFromUser(containers, "commit");
return containers.SingleOrDefault(c => c.ContainerName == identifier);
}
-}
\ No newline at end of file
+}
diff --git a/src/Commands/Commit/CommitSettings.cs b/src/Commands/Commit/CommitSettings.cs
index a4dad5f..7a9c411 100644
--- a/src/Commands/Commit/CommitSettings.cs
+++ b/src/Commands/Commit/CommitSettings.cs
@@ -7,12 +7,12 @@ public class CommitSettings : CommandSettings, IContainerIdentifierSettings
[CommandArgument(0, "[ContainerIdentifier]")]
public string? ContainerIdentifier { get; set; }
- [CommandOption("-t|--tag")]
+ [CommandOption("-t|--tag")]
public string? Tag { get; set; }
- [CommandOption("-s|--switch")]
+ [CommandOption("-s|--switch")]
public bool Switch { get; set; }
- [CommandOption("-o|--overwrite")]
+ [CommandOption("-o|--overwrite")]
public bool Overwrite { get; set; }
-}
\ No newline at end of file
+}
diff --git a/src/Commands/Commit/CreateImageFromContainerCommand.cs b/src/Commands/Commit/CreateImageFromContainerCommand.cs
index f72456c..f7a9139 100644
--- a/src/Commands/Commit/CreateImageFromContainerCommand.cs
+++ b/src/Commands/Commit/CreateImageFromContainerCommand.cs
@@ -3,27 +3,35 @@
namespace port.Commands.Commit;
-internal class CreateImageFromContainerCommand(IDockerClient dockerClient) : ICreateImageFromContainerCommand
+internal class CreateImageFromContainerCommand(IDockerClient dockerClient)
+ : ICreateImageFromContainerCommand
{
- public async Task ExecuteAsync(Container container, string imageName, string tagPrefix, string newTag)
+ public async Task ExecuteAsync(
+ Container container,
+ string imageName,
+ string tagPrefix,
+ string newTag
+ )
{
var labels = new Dictionary();
var identifier = container.GetLabel(Constants.IdentifierLabel);
- if (identifier is not null) labels.Add(Constants.IdentifierLabel, identifier);
+ if (identifier is not null)
+ labels.Add(Constants.IdentifierLabel, identifier);
var baseTag = container.GetLabel(Constants.BaseTagLabel);
- if (baseTag is not null) labels.Add(Constants.BaseTagLabel, baseTag);
+ if (baseTag is not null)
+ labels.Add(Constants.BaseTagLabel, baseTag);
labels.Add(Constants.TagPrefix, tagPrefix);
- if (baseTag == newTag) throw new InvalidOperationException("Can not overwrite base tags");
- await dockerClient.Images.CommitContainerChangesAsync(new CommitContainerChangesParameters
- {
- ContainerID = container.Id,
- RepositoryName = imageName,
- Tag = newTag,
- Config = new Docker.DotNet.Models.Config
+ if (baseTag == newTag)
+ throw new InvalidOperationException("Can not overwrite base tags");
+ await dockerClient.Images.CommitContainerChangesAsync(
+ new CommitContainerChangesParameters
{
- Labels = labels
+ ContainerID = container.Id,
+ RepositoryName = imageName,
+ Tag = newTag,
+ Config = new Docker.DotNet.Models.Config { Labels = labels },
}
- });
+ );
return newTag;
}
-}
\ No newline at end of file
+}
diff --git a/src/Commands/Commit/GetDigestsByIdQuery.cs b/src/Commands/Commit/GetDigestsByIdQuery.cs
index 227c396..d7b931f 100644
--- a/src/Commands/Commit/GetDigestsByIdQuery.cs
+++ b/src/Commands/Commit/GetDigestsByIdQuery.cs
@@ -16,11 +16,10 @@ public GetDigestsByIdQuery(IDockerClient dockerClient)
{
var parameters = new ImagesListParameters
{
- Filters = new Dictionary>()
+ Filters = new Dictionary>(),
};
var imagesListResponses = await _dockerClient.Images.ListImagesAsync(parameters);
- var imagesListResponse = imagesListResponses
- .SingleOrDefault(e => e.ID == imageId);
+ var imagesListResponse = imagesListResponses.SingleOrDefault(e => e.ID == imageId);
return imagesListResponse?.RepoDigests;
}
-}
\ No newline at end of file
+}
diff --git a/src/Commands/Commit/ICreateImageFromContainerCommand.cs b/src/Commands/Commit/ICreateImageFromContainerCommand.cs
index c7e5219..3ee0320 100644
--- a/src/Commands/Commit/ICreateImageFromContainerCommand.cs
+++ b/src/Commands/Commit/ICreateImageFromContainerCommand.cs
@@ -2,5 +2,10 @@ namespace port.Commands.Commit;
public interface ICreateImageFromContainerCommand
{
- Task ExecuteAsync(Container container, string imageName, string tagPrefix, string newTag);
-}
\ No newline at end of file
+ Task ExecuteAsync(
+ Container container,
+ string imageName,
+ string tagPrefix,
+ string newTag
+ );
+}
diff --git a/src/Commands/Commit/IGetDigestsByIdQuery.cs b/src/Commands/Commit/IGetDigestsByIdQuery.cs
index d9ed986..9eb6a46 100644
--- a/src/Commands/Commit/IGetDigestsByIdQuery.cs
+++ b/src/Commands/Commit/IGetDigestsByIdQuery.cs
@@ -3,4 +3,4 @@ namespace port.Commands.Commit;
internal interface IGetDigestsByIdQuery
{
Task?> QueryAsync(string imageId);
-}
\ No newline at end of file
+}
diff --git a/src/Commands/Config/ConfigCliCommand.cs b/src/Commands/Config/ConfigCliCommand.cs
index 5725fbf..4390fb6 100644
--- a/src/Commands/Config/ConfigCliCommand.cs
+++ b/src/Commands/Config/ConfigCliCommand.cs
@@ -22,5 +22,6 @@ public override int Execute(CommandContext context, ConfigSettings settings)
return 0;
}
- private static string FormatAsLink(string caption, string url) => $"\u001B]8;;{url}\a{caption}\u001B]8;;\a";
-}
\ No newline at end of file
+ private static string FormatAsLink(string caption, string url) =>
+ $"\u001B]8;;{url}\a{caption}\u001B]8;;\a";
+}
diff --git a/src/Commands/Config/ConfigSettings.cs b/src/Commands/Config/ConfigSettings.cs
index cce7f5b..ce9e2d5 100644
--- a/src/Commands/Config/ConfigSettings.cs
+++ b/src/Commands/Config/ConfigSettings.cs
@@ -6,4 +6,4 @@ internal class ConfigSettings : CommandSettings
{
[CommandOption("-o|--open")]
public bool Open { get; set; } = false;
-}
\ No newline at end of file
+}
diff --git a/src/Commands/List/ListCliCommand.cs b/src/Commands/List/ListCliCommand.cs
index 8d79b74..4947767 100644
--- a/src/Commands/List/ListCliCommand.cs
+++ b/src/Commands/List/ListCliCommand.cs
@@ -14,8 +14,10 @@ public ListCliCommand(IAllImagesQuery allImagesQuery)
public override async Task ExecuteAsync(CommandContext _, ListSettings settings)
{
- var textsGroups = await Spinner.StartAsync("Loading images",
- async _ => await CreateImageTree(settings.ImageIdentifier).ToListAsync());
+ var textsGroups = await Spinner.StartAsync(
+ "Loading images",
+ async _ => await CreateImageTree(settings.ImageIdentifier).ToListAsync()
+ );
AnsiConsole.WriteLine();
foreach (var text in textsGroups.SelectMany(texts => texts))
{
@@ -27,7 +29,10 @@ public override async Task ExecuteAsync(CommandContext _, ListSettings sett
public async Task ExecuteAsync()
{
- var textsGroups = await Spinner.StartAsync("Loading images", async _ => await CreateImageTree().ToListAsync());
+ var textsGroups = await Spinner.StartAsync(
+ "Loading images",
+ async _ => await CreateImageTree().ToListAsync()
+ );
AnsiConsole.WriteLine();
foreach (var text in textsGroups.SelectMany(texts => texts))
{
@@ -37,16 +42,20 @@ public async Task ExecuteAsync()
private async IAsyncEnumerable> CreateImageTree(string? imageIdentifier = default)
{
- var imageGroups = (await _allImagesQuery.QueryAsync().ToListAsync()).Where(e =>
- imageIdentifier == null || e.Identifier == imageIdentifier)
- .OrderBy(i => i.Identifier).ToList();
- var lengths = TagTextBuilder.GetLengths(imageGroups.SelectMany(imageGroup => imageGroup.Images));
+ var imageGroups = (await _allImagesQuery.QueryAsync().ToListAsync())
+ .Where(e => imageIdentifier == null || e.Identifier == imageIdentifier)
+ .OrderBy(i => i.Identifier)
+ .ToList();
+ var lengths = TagTextBuilder.GetLengths(
+ imageGroups.SelectMany(imageGroup => imageGroup.Images)
+ );
foreach (var imageGroup in imageGroups)
{
- yield return imageGroup.Images.Where(e => e.Tag != null)
+ yield return imageGroup
+ .Images.Where(e => e.Tag != null)
.OrderBy(e => e.Tag)
.Select(image => TagTextBuilder.BuildTagText(image, lengths))
.ToList();
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Commands/List/ListSettings.cs b/src/Commands/List/ListSettings.cs
index 356ebff..2c1a845 100644
--- a/src/Commands/List/ListSettings.cs
+++ b/src/Commands/List/ListSettings.cs
@@ -4,6 +4,6 @@ namespace port.Commands.List;
public class ListSettings : CommandSettings, IImageIdentifierSettings
{
- [CommandArgument(0, "[ImageIdentifier]")]
+ [CommandArgument(0, "[ImageIdentifier]")]
public string? ImageIdentifier { get; set; }
-}
\ No newline at end of file
+}
diff --git a/src/Commands/Prune/PruneCliCommand.cs b/src/Commands/Prune/PruneCliCommand.cs
index eefe61a..b3f6d1b 100644
--- a/src/Commands/Prune/PruneCliCommand.cs
+++ b/src/Commands/Prune/PruneCliCommand.cs
@@ -11,18 +11,23 @@ internal class PruneCliCommand : AsyncCommand
private readonly port.Config.Config _config;
private readonly IAllImagesQuery _allImagesQuery;
private readonly IRemoveImagesCliDependentCommand _removeImagesCliDependentCommand;
- private readonly ListCliCommand _listCliCommand;
+ private readonly ConditionalListCliCommand _conditionalListCliCommand;
- public PruneCliCommand(IImageIdentifierAndTagEvaluator imageIdentifierAndTagEvaluator,
- IGetImageIdQuery getImageIdQuery, port.Config.Config config, IAllImagesQuery allImagesQuery,
- IRemoveImagesCliDependentCommand removeImagesCliDependentCommand, ListCliCommand listCliCommand)
+ public PruneCliCommand(
+ IImageIdentifierAndTagEvaluator imageIdentifierAndTagEvaluator,
+ IGetImageIdQuery getImageIdQuery,
+ port.Config.Config config,
+ IAllImagesQuery allImagesQuery,
+ IRemoveImagesCliDependentCommand removeImagesCliDependentCommand,
+ ConditionalListCliCommand conditionalListCliCommand
+ )
{
_imageIdentifierAndTagEvaluator = imageIdentifierAndTagEvaluator;
_getImageIdQuery = getImageIdQuery;
_config = config;
_allImagesQuery = allImagesQuery;
_removeImagesCliDependentCommand = removeImagesCliDependentCommand;
- _listCliCommand = listCliCommand;
+ _conditionalListCliCommand = conditionalListCliCommand;
}
public override async Task ExecuteAsync(CommandContext context, PruneSettings settings)
@@ -30,16 +35,23 @@ public override async Task ExecuteAsync(CommandContext context, PruneSettin
var identifiers = GetIdentifiersAsync(settings);
await foreach (var identifier in identifiers)
{
- var result = await Spinner.StartAsync($"Removing untagged images for identifier '{identifier}'",
- ctx => RemoveUntaggedImagesAsync(identifier, ctx));
- foreach (var imageRemovalResult in result.Where(imageRemovalResult => !imageRemovalResult.Successful))
+ var result = await Spinner.StartAsync(
+ $"Removing untagged images for identifier '{identifier}'",
+ ctx => RemoveUntaggedImagesAsync(identifier, ctx)
+ );
+ foreach (
+ var imageRemovalResult in result.Where(imageRemovalResult =>
+ !imageRemovalResult.Successful
+ )
+ )
{
AnsiConsole.MarkupLine(
- $"[orange3]Unable to removed image with id '{imageRemovalResult.ImageId}'[/] because it has dependent child images");
+ $"[orange3]Unable to removed image with id '{imageRemovalResult.ImageId}'[/] because it has dependent child images"
+ );
}
}
- await _listCliCommand.ExecuteAsync();
+ await _conditionalListCliCommand.ExecuteAsync();
return 0;
}
@@ -48,26 +60,32 @@ private async IAsyncEnumerable GetIdentifiersAsync(IImageIdentifierSetti
{
if (settings.ImageIdentifier != null)
{
- yield return _imageIdentifierAndTagEvaluator.Evaluate(settings.ImageIdentifier).identifier;
+ yield return _imageIdentifierAndTagEvaluator
+ .Evaluate(settings.ImageIdentifier)
+ .identifier;
}
- await foreach (var identifier in _allImagesQuery
- .QueryAsync()
- .Where(e => e.Images.Any(i => i.Tag == null))
- .Select(e => e.Identifier))
+ await foreach (
+ var identifier in _allImagesQuery
+ .QueryAsync()
+ .Where(e => e.Images.Any(i => i.Tag == null))
+ .Select(e => e.Identifier)
+ )
{
yield return identifier;
}
}
- private async Task> RemoveUntaggedImagesAsync(string identifier, StatusContext ctx)
+ private async Task> RemoveUntaggedImagesAsync(
+ string identifier,
+ StatusContext ctx
+ )
{
var imageConfig = _config.GetImageConfigByIdentifier(identifier);
var imageName = imageConfig.ImageName;
var imageIds = (await _getImageIdQuery.QueryAsync(imageName, null)).ToList();
if (!imageIds.Any())
- throw new InvalidOperationException(
- "No images to remove found".EscapeMarkup());
+ throw new InvalidOperationException("No images to remove found".EscapeMarkup());
return await _removeImagesCliDependentCommand.ExecuteAsync(imageIds, ctx);
}
-}
\ No newline at end of file
+}
diff --git a/src/Commands/Prune/PruneSettings.cs b/src/Commands/Prune/PruneSettings.cs
index a6a0383..613d6e6 100644
--- a/src/Commands/Prune/PruneSettings.cs
+++ b/src/Commands/Prune/PruneSettings.cs
@@ -6,4 +6,4 @@ internal class PruneSettings : CommandSettings, IImageIdentifierSettings
{
[CommandArgument(0, "[ImageIdentifier]")]
public string? ImageIdentifier { get; set; }
-}
\ No newline at end of file
+}
diff --git a/src/Commands/Pull/PullCliCommand.cs b/src/Commands/Pull/PullCliCommand.cs
index 33c9bde..c991877 100644
--- a/src/Commands/Pull/PullCliCommand.cs
+++ b/src/Commands/Pull/PullCliCommand.cs
@@ -2,28 +2,34 @@
namespace port.Commands.Pull;
-public class PullCliCommand(
+internal class PullCliCommand(
IImageIdentifierPrompt imageIdentifierPrompt,
port.Config.Config config,
IImageIdentifierAndTagEvaluator imageIdentifierAndTagEvaluator,
- ICreateImageCliChildCommand createImageCliChildCommand)
- : AsyncCommand
+ ICreateImageCliChildCommand createImageCliChildCommand,
+ ConditionalListCliCommand conditionalListCliCommand
+) : AsyncCommand
{
public override async Task ExecuteAsync(CommandContext context, PullSettings settings)
{
var (identifier, tag) = await GetBaseIdentifierAndTagAsync(settings);
await PullImageAsync(identifier, tag);
+ await conditionalListCliCommand.ExecuteAsync();
return 0;
}
- private async Task<(string identifier, string? tag)> GetBaseIdentifierAndTagAsync(IImageIdentifierSettings settings)
+ private async Task<(string identifier, string? tag)> GetBaseIdentifierAndTagAsync(
+ IImageIdentifierSettings settings
+ )
{
if (settings.ImageIdentifier != null)
{
return imageIdentifierAndTagEvaluator.Evaluate(settings.ImageIdentifier);
}
- var identifierAndTag = await imageIdentifierPrompt.GetBaseIdentifierAndTagFromUserAsync("pull");
+ var identifierAndTag = await imageIdentifierPrompt.GetBaseIdentifierAndTagFromUserAsync(
+ "pull"
+ );
return (identifierAndTag.identifier, identifierAndTag.tag);
}
@@ -32,11 +38,13 @@ private async Task PullImageAsync(string identifier, string? tag)
var imageConfig = config.GetImageConfigByIdentifier(identifier);
if (imageConfig == null)
{
- throw new ArgumentException($"There is no config defined for identifier '{identifier}'",
- nameof(identifier));
+ throw new ArgumentException(
+ $"There is no config defined for identifier '{identifier}'",
+ nameof(identifier)
+ );
}
var imageName = imageConfig.ImageName;
await createImageCliChildCommand.ExecuteAsync(imageName, tag);
}
-}
\ No newline at end of file
+}
diff --git a/src/Commands/Pull/PullSettings.cs b/src/Commands/Pull/PullSettings.cs
index 0d2854f..0407f00 100644
--- a/src/Commands/Pull/PullSettings.cs
+++ b/src/Commands/Pull/PullSettings.cs
@@ -4,6 +4,6 @@ namespace port.Commands.Pull;
public class PullSettings : CommandSettings, IImageIdentifierSettings
{
- [CommandArgument(0, "[ImageIdentifier]")]
+ [CommandArgument(0, "[ImageIdentifier]")]
public string? ImageIdentifier { get; set; }
-}
\ No newline at end of file
+}
diff --git a/src/Commands/Remove/RemoveCliCommand.cs b/src/Commands/Remove/RemoveCliCommand.cs
index a478c71..a2f7281 100644
--- a/src/Commands/Remove/RemoveCliCommand.cs
+++ b/src/Commands/Remove/RemoveCliCommand.cs
@@ -12,12 +12,17 @@ internal class RemoveCliCommand : AsyncCommand
private readonly IImageIdentifierAndTagEvaluator _imageIdentifierAndTagEvaluator;
private readonly IAllImagesQuery _allImagesQuery;
private readonly IRemoveImagesCliDependentCommand _removeImagesCliDependentCommand;
- private readonly ListCliCommand _listCliCommand;
+ private readonly ConditionalListCliCommand _conditionalListCliCommand;
- public RemoveCliCommand(IImageIdentifierPrompt imageIdentifierPrompt, port.Config.Config config,
- IImageIdentifierAndTagEvaluator imageIdentifierAndTagEvaluator, IGetImageIdQuery getImageIdQuery,
- IAllImagesQuery allImagesQuery, IRemoveImagesCliDependentCommand removeImagesCliDependentCommand,
- ListCliCommand listCliCommand)
+ public RemoveCliCommand(
+ IImageIdentifierPrompt imageIdentifierPrompt,
+ port.Config.Config config,
+ IImageIdentifierAndTagEvaluator imageIdentifierAndTagEvaluator,
+ IGetImageIdQuery getImageIdQuery,
+ IAllImagesQuery allImagesQuery,
+ IRemoveImagesCliDependentCommand removeImagesCliDependentCommand,
+ ConditionalListCliCommand conditionalListCliCommand
+ )
{
_imageIdentifierPrompt = imageIdentifierPrompt;
_config = config;
@@ -25,77 +30,98 @@ public RemoveCliCommand(IImageIdentifierPrompt imageIdentifierPrompt, port.Confi
_getImageIdQuery = getImageIdQuery;
_allImagesQuery = allImagesQuery;
_removeImagesCliDependentCommand = removeImagesCliDependentCommand;
- _listCliCommand = listCliCommand;
+ _conditionalListCliCommand = conditionalListCliCommand;
}
public override async Task ExecuteAsync(CommandContext context, RemoveSettings settings)
{
var (identifier, tag) = await GetIdentifierAndTagAsync(settings);
- var result = await Spinner.StartAsync($"Removing {ImageNameHelper.BuildImageName(identifier, tag)}",
- async ctx =>
- {
- var imageConfig = _config.GetImageConfigByIdentifier(identifier);
- var imageName = imageConfig.ImageName;
- var initialImageIds = new List();
- if (tag is not null && !imageConfig.ImageTags.Contains(tag))
+ var result = await Spinner
+ .StartAsync(
+ $"Removing {ImageNameHelper.BuildImageName(identifier, tag)}",
+ async ctx =>
{
- initialImageIds.AddRange(await _getImageIdQuery.QueryAsync(imageName, $"{TagPrefixHelper.GetTagPrefix(identifier)}{tag}"));
- }
-
- initialImageIds.AddRange(await _getImageIdQuery.QueryAsync(imageName, tag));
+ var imageConfig = _config.GetImageConfigByIdentifier(identifier);
+ var imageName = imageConfig.ImageName;
+ var initialImageIds = new List();
+ if (tag is not null && !imageConfig.ImageTags.Contains(tag))
+ {
+ initialImageIds.AddRange(
+ await _getImageIdQuery.QueryAsync(
+ imageName,
+ $"{TagPrefixHelper.GetTagPrefix(identifier)}{tag}"
+ )
+ );
+ }
- var imageIds = new List();
- if (settings.Recursive)
- {
- var images = (await _allImagesQuery.QueryAllImagesWithParentAsync().ToListAsync())
- .Where(e => e is { Id: not null, ParentId: not null })
- .ToList();
- var imageIdsToAnalyze = initialImageIds.ToHashSet();
- while (imageIdsToAnalyze.Count != 0)
+ initialImageIds.AddRange(await _getImageIdQuery.QueryAsync(imageName, tag));
+
+ var imageIds = new List();
+ if (settings.Recursive)
{
- imageIds.AddRange(imageIdsToAnalyze);
- var analyze = imageIdsToAnalyze;
- var childImageIds = images
- .Where(e => analyze.Contains(e.ParentId))
- .Select(e => e.Id)
- .ToHashSet();
- imageIdsToAnalyze = childImageIds;
+ var images = (
+ await _allImagesQuery.QueryAllImagesWithParentAsync().ToListAsync()
+ )
+ .Where(e => e is { Id: not null, ParentId: not null })
+ .ToList();
+ var imageIdsToAnalyze = initialImageIds.ToHashSet();
+ while (imageIdsToAnalyze.Count != 0)
+ {
+ imageIds.AddRange(imageIdsToAnalyze);
+ var analyze = imageIdsToAnalyze;
+ var childImageIds = images
+ .Where(e => analyze.Contains(e.ParentId))
+ .Select(e => e.Id)
+ .ToHashSet();
+ imageIdsToAnalyze = childImageIds;
+ }
+
+ imageIds.Reverse();
+ }
+ else
+ {
+ imageIds = initialImageIds.ToList();
}
- imageIds.Reverse();
+ if (imageIds.Count == 0)
+ throw new InvalidOperationException(
+ "No images to remove found".EscapeMarkup()
+ );
+ return _removeImagesCliDependentCommand.ExecuteAsync(imageIds, ctx);
}
- else
- {
- imageIds = initialImageIds.ToList();
- }
-
- if (imageIds.Count == 0)
- throw new InvalidOperationException(
- "No images to remove found".EscapeMarkup());
- return _removeImagesCliDependentCommand.ExecuteAsync(imageIds, ctx);
- }).Unwrap();
- foreach (var imageRemovalResult in result.Where(imageRemovalResult => !imageRemovalResult.Successful))
+ )
+ .Unwrap();
+ foreach (
+ var imageRemovalResult in result.Where(imageRemovalResult =>
+ !imageRemovalResult.Successful
+ )
+ )
{
AnsiConsole.MarkupLine(
- $"[orange3]Unable to removed image with id '{imageRemovalResult.ImageId}'[/] because it has dependent children");
+ $"[orange3]Unable to removed image with id '{imageRemovalResult.ImageId}'[/] because it has dependent children"
+ );
if (settings.Recursive)
AnsiConsole.MarkupLine(
- "That may be because an child image is based on an [red]unknown image[/] which can not be removed automatically, manually remove it and try again");
+ "That may be because an child image is based on an [red]unknown image[/] which can not be removed automatically, manually remove it and try again"
+ );
}
- await _listCliCommand.ExecuteAsync();
+ await _conditionalListCliCommand.ExecuteAsync();
return 0;
}
- private async Task<(string identifier, string? tag)> GetIdentifierAndTagAsync(IImageIdentifierSettings settings)
+ private async Task<(string identifier, string? tag)> GetIdentifierAndTagAsync(
+ IImageIdentifierSettings settings
+ )
{
if (settings.ImageIdentifier != null)
{
return _imageIdentifierAndTagEvaluator.Evaluate(settings.ImageIdentifier);
}
- var identifierAndTag = await _imageIdentifierPrompt.GetDownloadedIdentifierAndTagFromUserAsync("remove");
+ var identifierAndTag =
+ await _imageIdentifierPrompt.GetDownloadedIdentifierAndTagFromUserAsync("remove");
return (identifierAndTag.identifier, identifierAndTag.tag);
}
-}
\ No newline at end of file
+}
diff --git a/src/Commands/Remove/RemoveSettings.cs b/src/Commands/Remove/RemoveSettings.cs
index 0a790b9..0aebe76 100644
--- a/src/Commands/Remove/RemoveSettings.cs
+++ b/src/Commands/Remove/RemoveSettings.cs
@@ -4,9 +4,9 @@ namespace port.Commands.Remove;
public class RemoveSettings : CommandSettings, IImageIdentifierSettings
{
- [CommandArgument(0, "[ImageIdentifier]")]
+ [CommandArgument(0, "[ImageIdentifier]")]
public string? ImageIdentifier { get; set; }
-
+
[CommandOption("-r|--recursive")]
public bool Recursive { get; set; }
-}
\ No newline at end of file
+}
diff --git a/src/Commands/Reset/ResetCliCommand.cs b/src/Commands/Reset/ResetCliCommand.cs
index a35da89..657f1ff 100644
--- a/src/Commands/Reset/ResetCliCommand.cs
+++ b/src/Commands/Reset/ResetCliCommand.cs
@@ -9,8 +9,8 @@ internal class ResetCliCommand(
ICreateContainerCommand createContainerCommand,
IRunContainerCommand runContainerCommand,
IContainerNamePrompt containerNamePrompt,
- ListCliCommand listCliCommand)
- : AsyncCommand
+ ConditionalListCliCommand conditionalListCliCommand
+) : AsyncCommand
{
public override async Task ExecuteAsync(CommandContext context, ResetSettings settings)
{
@@ -22,15 +22,17 @@ public override async Task ExecuteAsync(CommandContext context, ResetSettin
await ResetContainerAsync(container);
- await listCliCommand.ExecuteAsync();
+ await conditionalListCliCommand.ExecuteAsync();
return 0;
}
private async Task GetContainerAsync(IContainerIdentifierSettings settings)
{
- var containers = await Spinner.StartAsync("Getting running containers",
- async _ => await getRunningContainersQuery.QueryAsync().ToListAsync());
+ var containers = await Spinner.StartAsync(
+ "Getting running containers",
+ async _ => await getRunningContainersQuery.QueryAsync().ToListAsync()
+ );
if (settings.ContainerIdentifier != null)
{
@@ -41,7 +43,6 @@ public override async Task ExecuteAsync(CommandContext context, ResetSettin
return containers.SingleOrDefault(c => c.ContainerName == identifier);
}
-
private async Task ResetContainerAsync(Container container)
{
await Spinner.StartAsync(
@@ -51,6 +52,7 @@ await Spinner.StartAsync(
await stopAndRemoveContainerCommand.ExecuteAsync(container.Id);
var id = await createContainerCommand.ExecuteAsync(container);
await runContainerCommand.ExecuteAsync(id);
- });
+ }
+ );
}
-}
\ No newline at end of file
+}
diff --git a/src/Commands/Reset/ResetSettings.cs b/src/Commands/Reset/ResetSettings.cs
index 251aea5..815773f 100644
--- a/src/Commands/Reset/ResetSettings.cs
+++ b/src/Commands/Reset/ResetSettings.cs
@@ -6,4 +6,4 @@ internal class ResetSettings : CommandSettings, IContainerIdentifierSettings
{
[CommandArgument(0, "[ContainerIdentifier]")]
public string? ContainerIdentifier { get; set; }
-}
\ No newline at end of file
+}
diff --git a/src/Commands/Run/RunCliCommand.cs b/src/Commands/Run/RunCliCommand.cs
index 79a1c27..91e4f88 100644
--- a/src/Commands/Run/RunCliCommand.cs
+++ b/src/Commands/Run/RunCliCommand.cs
@@ -15,8 +15,8 @@ internal class RunCliCommand(
port.Config.Config config,
IImageIdentifierAndTagEvaluator imageIdentifierAndTagEvaluator,
IStopAndRemoveContainerCommand stopAndRemoveContainerCommand,
- ListCliCommand listCliCommand)
- : AsyncCommand
+ ConditionalListCliCommand conditionalListCliCommand
+) : AsyncCommand
{
private const char PortSeparator = ':';
@@ -29,19 +29,23 @@ public override async Task ExecuteAsync(CommandContext context, RunSettings
await TerminateOtherContainersAsync(identifier);
await LaunchImageAsync(identifier, tag, settings.Reset);
- await listCliCommand.ExecuteAsync();
+ await conditionalListCliCommand.ExecuteAsync();
return 0;
}
- private async Task<(string identifier, string? tag)> GetIdentifierAndTagAsync(IImageIdentifierSettings settings)
+ private async Task<(string identifier, string? tag)> GetIdentifierAndTagAsync(
+ IImageIdentifierSettings settings
+ )
{
if (settings.ImageIdentifier != null)
{
return imageIdentifierAndTagEvaluator.Evaluate(settings.ImageIdentifier);
}
- var identifierAndTag = await imageIdentifierPrompt.GetRunnableIdentifierAndTagFromUserAsync("run");
+ var identifierAndTag = await imageIdentifierPrompt.GetRunnableIdentifierAndTagFromUserAsync(
+ "run"
+ );
return (identifierAndTag.identifier, identifierAndTag.tag);
}
@@ -50,35 +54,44 @@ private Task TerminateOtherContainersAsync(string identifier)
var imageConfig = config.GetImageConfigByIdentifier(identifier);
if (imageConfig == null)
{
- throw new ArgumentException($"There is no config defined for identifier '{identifier}'",
- nameof(identifier));
+ throw new ArgumentException(
+ $"There is no config defined for identifier '{identifier}'",
+ nameof(identifier)
+ );
}
- var hostPorts = imageConfig.Ports
- .Select(e => e.Split(PortSeparator)[0])
- .ToList();
- var spinnerTex = $"Terminating containers using host ports '{string.Join(", ", hostPorts)}'";
- return Spinner.StartAsync(spinnerTex, async _ =>
- {
- var containers = GetRunningContainersUsingHostPortsAsync(hostPorts);
- await foreach (var container in containers)
- await stopContainerCommand.ExecuteAsync(container.Id);
- });
+ var hostPorts = imageConfig.Ports.Select(e => e.Split(PortSeparator)[0]).ToList();
+ var spinnerTex =
+ $"Terminating containers using host ports '{string.Join(", ", hostPorts)}'";
+ return Spinner.StartAsync(
+ spinnerTex,
+ async _ =>
+ {
+ var containers = GetRunningContainersUsingHostPortsAsync(hostPorts);
+ await foreach (var container in containers)
+ await stopContainerCommand.ExecuteAsync(container.Id);
+ }
+ );
}
private IAsyncEnumerable GetRunningContainersUsingHostPortsAsync(
- IEnumerable hostPorts)
+ IEnumerable hostPorts
+ )
{
- return getContainersQuery.QueryRunningAsync()
+ return getContainersQuery
+ .QueryRunningAsync()
.Where(container =>
{
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
- if (container.PortBindings is null) return false;
- var usedHostPorts = container.PortBindings
- .SelectMany(pb => pb.Value
- .Select(hp => hp.HostPort));
- return container.PortBindings
- .Any(p => { return hostPorts.Any(p => usedHostPorts.Contains(p)); });
+ if (container.PortBindings is null)
+ return false;
+ var usedHostPorts = container.PortBindings.SelectMany(pb =>
+ pb.Value.Select(hp => hp.HostPort)
+ );
+ return container.PortBindings.Any(p =>
+ {
+ return hostPorts.Any(p => usedHostPorts.Contains(p));
+ });
});
}
@@ -89,47 +102,69 @@ private async Task LaunchImageAsync(string identifier, string tag, bool resetCon
var imageName = imageConfig.ImageName;
var containerName = ContainerNameHelper.BuildContainerName(identifier, tag);
- var existingImage = await Spinner.StartAsync($"Query existing image: {constructedImageName}",
+ var existingImage = await Spinner.StartAsync(
+ $"Query existing image: {constructedImageName}",
async _ =>
{
- if (imageConfig.ImageTags.Contains(tag)) return await getImageQuery.QueryAsync(imageName, tag);
+ if (imageConfig.ImageTags.Contains(tag))
+ return await getImageQuery.QueryAsync(imageName, tag);
var existingImage = await getImageQuery.QueryAsync(imageName, tag);
- if (existingImage is not null) return existingImage;
+ if (existingImage is not null)
+ return existingImage;
tag = $"{TagPrefixHelper.GetTagPrefix(identifier)}{tag}";
return await getImageQuery.QueryAsync(imageName, tag);
- });
+ }
+ );
if (existingImage is null)
{
await createImageCliChildCommand.ExecuteAsync(imageName, tag);
- existingImage = await Spinner.StartAsync($"Re-query existing image: {constructedImageName}",
- async _ => await getImageQuery.QueryAsync(imageName, tag));
+ existingImage = await Spinner.StartAsync(
+ $"Re-query existing image: {constructedImageName}",
+ async _ => await getImageQuery.QueryAsync(imageName, tag)
+ );
}
var tagPrefix = existingImage?.GetLabel(Constants.TagPrefix);
- await Spinner.StartAsync($"Launching {constructedImageName}", async _ =>
- {
- var containers = await getContainersQuery.QueryByContainerNameAsync(containerName).ToListAsync();
- var ports = imageConfig.Ports;
- var environment = imageConfig.Environment;
- if (containers.Count == 1 && resetContainer)
- {
- await stopAndRemoveContainerCommand.ExecuteAsync(containers.Single().Id);
- var id =
- await createContainerCommand.ExecuteAsync(identifier, imageName, tagPrefix, tag, ports,
- environment);
- await runContainerCommand.ExecuteAsync(id);
- }
- else if (containers.Count == 1 && !resetContainer)
- {
- await runContainerCommand.ExecuteAsync(containers.Single().Id);
- }
- else if (containers.Count == 0)
+ await Spinner.StartAsync(
+ $"Launching {constructedImageName}",
+ async _ =>
{
- var id = await createContainerCommand.ExecuteAsync(identifier, imageName, tagPrefix, tag, ports,
- environment);
- await runContainerCommand.ExecuteAsync(id);
+ var containers = await getContainersQuery
+ .QueryByContainerNameAsync(containerName)
+ .ToListAsync();
+ var ports = imageConfig.Ports;
+ var environment = imageConfig.Environment;
+ if (containers.Count == 1 && resetContainer)
+ {
+ await stopAndRemoveContainerCommand.ExecuteAsync(containers.Single().Id);
+ var id = await createContainerCommand.ExecuteAsync(
+ identifier,
+ imageName,
+ tagPrefix,
+ tag,
+ ports,
+ environment
+ );
+ await runContainerCommand.ExecuteAsync(id);
+ }
+ else if (containers.Count == 1 && !resetContainer)
+ {
+ await runContainerCommand.ExecuteAsync(containers.Single().Id);
+ }
+ else if (containers.Count == 0)
+ {
+ var id = await createContainerCommand.ExecuteAsync(
+ identifier,
+ imageName,
+ tagPrefix,
+ tag,
+ ports,
+ environment
+ );
+ await runContainerCommand.ExecuteAsync(id);
+ }
}
- });
+ );
}
-}
\ No newline at end of file
+}
diff --git a/src/Commands/Run/RunSettings.cs b/src/Commands/Run/RunSettings.cs
index 7424327..b658af4 100644
--- a/src/Commands/Run/RunSettings.cs
+++ b/src/Commands/Run/RunSettings.cs
@@ -6,7 +6,7 @@ public class RunSettings : CommandSettings, IImageIdentifierSettings
{
[CommandArgument(0, "[ImageIdentifier]")]
public string? ImageIdentifier { get; set; }
-
+
[CommandOption("-r|--reset")]
public bool Reset { get; set; }
-}
\ No newline at end of file
+}
diff --git a/src/Commands/Stop/StopCliCommand.cs b/src/Commands/Stop/StopCliCommand.cs
index 77e2850..e0766f4 100644
--- a/src/Commands/Stop/StopCliCommand.cs
+++ b/src/Commands/Stop/StopCliCommand.cs
@@ -9,16 +9,19 @@ internal class StopCliCommand : AsyncCommand
private readonly IGetRunningContainersQuery _getRunningContainersQuery;
private readonly IContainerNamePrompt _containerNamePrompt;
private readonly IStopContainerCommand _stopContainerCommand;
- private readonly ListCliCommand _listCliCommand;
+ private readonly ConditionalListCliCommand _conditionalListCliCommand;
- public StopCliCommand(IGetRunningContainersQuery getRunningContainersQuery,
- IContainerNamePrompt containerNamePrompt, IStopContainerCommand stopContainerCommand,
- ListCliCommand listCliCommand)
+ public StopCliCommand(
+ IGetRunningContainersQuery getRunningContainersQuery,
+ IContainerNamePrompt containerNamePrompt,
+ IStopContainerCommand stopContainerCommand,
+ ConditionalListCliCommand conditionalListCliCommand
+ )
{
_getRunningContainersQuery = getRunningContainersQuery;
_containerNamePrompt = containerNamePrompt;
_stopContainerCommand = stopContainerCommand;
- _listCliCommand = listCliCommand;
+ _conditionalListCliCommand = conditionalListCliCommand;
}
public override async Task ExecuteAsync(CommandContext context, StopSettings settings)
@@ -31,7 +34,7 @@ public override async Task ExecuteAsync(CommandContext context, StopSetting
await StopContainerAsync(container);
- await _listCliCommand.ExecuteAsync();
+ await _conditionalListCliCommand.ExecuteAsync();
return 0;
}
@@ -48,11 +51,14 @@ public override async Task ExecuteAsync(CommandContext context, StopSetting
return containers.SingleOrDefault(c => c.ContainerName == identifier);
}
-
private async Task StopContainerAsync(Container container)
{
await Spinner.StartAsync(
- $"Stopping container '{container.ContainerName}'",
- async _ => { await _stopContainerCommand.ExecuteAsync(container.Id); });
+ $"Stopping container '{container.ContainerName}'",
+ async _ =>
+ {
+ await _stopContainerCommand.ExecuteAsync(container.Id);
+ }
+ );
}
-}
\ No newline at end of file
+}
diff --git a/src/Commands/Stop/StopSettings.cs b/src/Commands/Stop/StopSettings.cs
index a6fa6d7..2098164 100644
--- a/src/Commands/Stop/StopSettings.cs
+++ b/src/Commands/Stop/StopSettings.cs
@@ -6,4 +6,4 @@ internal class StopSettings : CommandSettings, IContainerIdentifierSettings
{
[CommandArgument(0, "[ContainerIdentifier]")]
public string? ContainerIdentifier { get; set; }
-}
\ No newline at end of file
+}
diff --git a/src/ConditionalListCliCommand.cs b/src/ConditionalListCliCommand.cs
new file mode 100644
index 0000000..58fe15b
--- /dev/null
+++ b/src/ConditionalListCliCommand.cs
@@ -0,0 +1,45 @@
+using port.Commands.List;
+
+namespace port;
+
+///
+/// Wrapper for ListCliCommand that respects command chaining detection.
+/// When commands are chained together, only the last command should display the list output.
+///
+internal class ConditionalListCliCommand
+{
+ private readonly ListCliCommand _listCliCommand;
+ private readonly ICommandChainDetector _commandChainDetector;
+
+ public ConditionalListCliCommand(
+ ListCliCommand listCliCommand,
+ ICommandChainDetector commandChainDetector
+ )
+ {
+ _listCliCommand = listCliCommand;
+ _commandChainDetector = commandChainDetector;
+ }
+
+ ///
+ /// Executes the list command only if we should display output (i.e., not in the middle of a command chain).
+ ///
+ public async Task ExecuteAsync()
+ {
+ if (_commandChainDetector.ShouldDisplayOutput())
+ {
+ await _listCliCommand.ExecuteAsync();
+ }
+ }
+
+ ///
+ /// Executes the list command with settings only if we should display output.
+ ///
+ public async Task ExecuteAsync(ListSettings settings)
+ {
+ if (_commandChainDetector.ShouldDisplayOutput())
+ {
+ return await _listCliCommand.ExecuteAsync(null!, settings);
+ }
+ return 0;
+ }
+}
diff --git a/src/Config/Config.cs b/src/Config/Config.cs
index 60d9dce..74248be 100644
--- a/src/Config/Config.cs
+++ b/src/Config/Config.cs
@@ -11,8 +11,10 @@ public ImageConfig GetImageConfigByIdentifier(string identifier)
var imageConfig = ImageConfigs.SingleOrDefault(e => e.Identifier == identifier);
if (imageConfig == null)
{
- throw new ArgumentException($"There is no config defined for identifier '{identifier}'",
- nameof(identifier));
+ throw new ArgumentException(
+ $"There is no config defined for identifier '{identifier}'",
+ nameof(identifier)
+ );
}
return imageConfig;
@@ -26,4 +28,4 @@ public class ImageConfig
public List Ports { get; set; } = new();
public List Environment { get; set; } = new();
}
-}
\ No newline at end of file
+}
diff --git a/src/Config/Config10.cs b/src/Config/Config10.cs
index 2eddc31..87197c5 100644
--- a/src/Config/Config10.cs
+++ b/src/Config/Config10.cs
@@ -11,8 +11,10 @@ public ImageConfig GetImageByImageName(string imageName)
var imageConfig = Images.SingleOrDefault(e => e.ImageName == imageName);
if (imageConfig == null)
{
- throw new ArgumentException($"There is no config defined for imageName '{imageName}'",
- nameof(imageName));
+ throw new ArgumentException(
+ $"There is no config defined for imageName '{imageName}'",
+ nameof(imageName)
+ );
}
return imageConfig;
@@ -23,13 +25,15 @@ public ImageConfig GetImageConfigByIdentifier(string identifier)
var imageConfig = Images.SingleOrDefault(e => e.Identifier == identifier);
if (imageConfig == null)
{
- throw new ArgumentException($"There is no config defined for identifier '{identifier}'",
- nameof(identifier));
+ throw new ArgumentException(
+ $"There is no config defined for identifier '{identifier}'",
+ nameof(identifier)
+ );
}
return imageConfig;
}
-
+
public class ImageConfig
{
public string Identifier { get; set; } = null!;
@@ -37,4 +41,4 @@ public class ImageConfig
public string ImageTag { get; set; } = null!;
public List Ports { get; set; } = new();
}
-}
\ No newline at end of file
+}
diff --git a/src/Config/ConfigFactory.cs b/src/Config/ConfigFactory.cs
index 374de82..4fbb54d 100644
--- a/src/Config/ConfigFactory.cs
+++ b/src/Config/ConfigFactory.cs
@@ -14,7 +14,7 @@ public static Config GetOrCreateConfig()
var configFilePath = GetConfigFilePath();
RenameIfNecessary(configFilePath);
-
+
if (File.Exists(configFilePath))
{
MigrateIfNecessary(configFilePath);
@@ -59,7 +59,10 @@ private static void MigrateIfNecessary(string path)
{
case Versions.V10:
var yaml = File.ReadAllText(path);
- PersistConfig(ConfigMigrations.Migrate10To11(serializer.Deserialize(yaml)), path);
+ PersistConfig(
+ ConfigMigrations.Migrate10To11(serializer.Deserialize(yaml)),
+ path
+ );
break;
case Versions.V11:
break;
@@ -75,32 +78,24 @@ private static Config LoadConfig(string path)
return serializer.Deserialize(yaml);
}
- private static Config CreateDefault() => new Config
- {
- DockerEndpoint = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
- ? "npipe://./pipe/docker_engine"
- : "unix:///var/run/docker.sock",
- ImageConfigs = new List
+ private static Config CreateDefault() =>
+ new Config
{
- new()
+ DockerEndpoint = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
+ ? "npipe://./pipe/docker_engine"
+ : "unix:///var/run/docker.sock",
+ ImageConfigs = new List
{
- Identifier = "Getting.Started",
- ImageName = "docker/getting-started",
- ImageTags = new List
- {
- "latest"
- },
- Ports = new List
+ new()
{
- "80:80"
+ Identifier = "Getting.Started",
+ ImageName = "docker/getting-started",
+ ImageTags = new List { "latest" },
+ Ports = new List { "80:80" },
+ Environment = new List { "80:80" },
},
- Environment = new List
- {
- "80:80"
- }
- }
- }
- };
+ },
+ };
private static void PersistConfig(Config config, string path)
{
@@ -110,9 +105,9 @@ private static void PersistConfig(Config config, string path)
var yaml = serializer.Serialize(config);
File.WriteAllText(path, yaml);
}
-
+
public class ConfigVersion
{
public string Version { get; set; } = null!;
}
-}
\ No newline at end of file
+}
diff --git a/src/Config/ConfigMigrations.cs b/src/Config/ConfigMigrations.cs
index aff0e01..0c27f1b 100644
--- a/src/Config/ConfigMigrations.cs
+++ b/src/Config/ConfigMigrations.cs
@@ -7,16 +7,15 @@ public static Config Migrate10To11(Config10 config)
return new Config
{
DockerEndpoint = config.DockerEndpoint,
- ImageConfigs = config.Images.Select(e => new Config.ImageConfig
- {
- Identifier = e.Identifier,
- ImageName = e.ImageName,
- Ports = e.Ports,
- ImageTags = new List
+ ImageConfigs = config
+ .Images.Select(e => new Config.ImageConfig
{
- e.ImageTag
- }
- }).ToList()
+ Identifier = e.Identifier,
+ ImageName = e.ImageName,
+ Ports = e.Ports,
+ ImageTags = new List { e.ImageTag },
+ })
+ .ToList(),
};
}
-}
\ No newline at end of file
+}
diff --git a/src/Config/ImageConfig.cs b/src/Config/ImageConfig.cs
index 4a25dcf..9c75e6b 100644
--- a/src/Config/ImageConfig.cs
+++ b/src/Config/ImageConfig.cs
@@ -1,2 +1 @@
namespace port.Config;
-
diff --git a/src/Config/Versions.cs b/src/Config/Versions.cs
index c0af2b8..731b7fa 100644
--- a/src/Config/Versions.cs
+++ b/src/Config/Versions.cs
@@ -4,4 +4,4 @@ public static class Versions
{
public const string V10 = "1.0";
public const string V11 = "1.1";
-}
\ No newline at end of file
+}
diff --git a/src/Constants.cs b/src/Constants.cs
index 8433b75..e88aea7 100644
--- a/src/Constants.cs
+++ b/src/Constants.cs
@@ -4,5 +4,5 @@ public static class Constants
{
public const string BaseTagLabel = "com.port.base.tag";
public const string IdentifierLabel = "com.port.identifier";
- public const string TagPrefix ="com.port.tag.prefix";
-}
\ No newline at end of file
+ public const string TagPrefix = "com.port.tag.prefix";
+}
diff --git a/src/ContainerNameHelper.cs b/src/ContainerNameHelper.cs
index a4d28c1..372d4f9 100644
--- a/src/ContainerNameHelper.cs
+++ b/src/ContainerNameHelper.cs
@@ -4,7 +4,10 @@ public static class ContainerNameHelper
{
private const string Separator = ".";
- public static bool TryGetContainerNameAndTag(string containerName, out (string containerName, string tag) nameAndTag)
+ public static bool TryGetContainerNameAndTag(
+ string containerName,
+ out (string containerName, string tag) nameAndTag
+ )
{
nameAndTag = (containerName, string.Empty);
var idx = containerName.LastIndexOf(Separator, StringComparison.Ordinal);
@@ -16,6 +19,7 @@ public static bool TryGetContainerNameAndTag(string containerName, out (string c
nameAndTag = (containerName[..idx], containerName[(idx + 1)..]);
return true;
}
-
- public static string BuildContainerName(string identifier, string? tag) => $"{identifier}{Separator}{tag}";
-}
\ No newline at end of file
+
+ public static string BuildContainerName(string identifier, string? tag) =>
+ $"{identifier}{Separator}{tag}";
+}
diff --git a/src/ContainerNamePrompt.cs b/src/ContainerNamePrompt.cs
index 5b5b5a5..80ac6ea 100644
--- a/src/ContainerNamePrompt.cs
+++ b/src/ContainerNamePrompt.cs
@@ -4,8 +4,10 @@ namespace port;
internal class ContainerNamePrompt : IContainerNamePrompt
{
- public string GetIdentifierOfContainerFromUser(IReadOnlyCollection containers,
- string command)
+ public string GetIdentifierOfContainerFromUser(
+ IReadOnlyCollection containers,
+ string command
+ )
{
switch (containers.Count)
{
@@ -32,13 +34,12 @@ private static SelectionPrompt