From 855877f6a68d9ef9a0a0569478c12fdecd404201 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Grzegorz=20=C5=81uszczek?= Date: Sun, 22 Mar 2026 13:03:09 +0100 Subject: [PATCH] Fuzzy container name matching in console command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Falls back to substring match when exact name isn't found. Auto-selects if unambiguous (e.g. "librarian" → "librarian-librarian-1"), errors with a disambiguation list if multiple containers match. Co-Authored-By: Claude Sonnet 4.6 --- lib/portainer_cli/commands/console.rb | 32 +++++++++++++++++++++------ 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/lib/portainer_cli/commands/console.rb b/lib/portainer_cli/commands/console.rb index 68d2f4d..0e2ae43 100644 --- a/lib/portainer_cli/commands/console.rb +++ b/lib/portainer_cli/commands/console.rb @@ -67,17 +67,35 @@ module PortainerCli def resolve_container(endpoint_id, name_or_id) containers = @client.get("/api/endpoints/#{endpoint_id}/docker/containers/json", all: 1) - match = containers.find do |c| + + # 1. Exact match: full name or ID prefix + exact = containers.find do |c| c['Id'].start_with?(name_or_id) || Array(c['Names']).any? { |n| n.delete_prefix('/') == name_or_id } end - unless match - available = containers.map do |c| - " #{c['Id'][0, 12]} #{Array(c['Names']).first&.delete_prefix('/')}" - end.join("\n") - error("Container '#{name_or_id}' not found on endpoint #{endpoint_id}. Available:\n#{available}") + return exact['Id'] if exact + + # 2. Fuzzy: container name contains the search term as a word segment + # Prefers "--" pattern, falls back to any substring match + names_for = ->(c) { Array(c['Names']).map { |n| n.delete_prefix('/') } } + + fuzzy = containers.select do |c| + names_for.(c).any? { |n| n.include?(name_or_id) } end - match['Id'] + + if fuzzy.size == 1 + matched_name = names_for.(fuzzy.first).first + $stderr.puts dim("Matched '#{name_or_id}' → #{matched_name}") + return fuzzy.first['Id'] + end + + if fuzzy.size > 1 + list = fuzzy.map { |c| " #{c['Id'][0, 12]} #{names_for.(c).first}" }.join("\n") + error("Ambiguous container '#{name_or_id}'. Did you mean one of:\n#{list}") + end + + available = containers.map { |c| " #{c['Id'][0, 12]} #{names_for.(c).first}" }.join("\n") + error("Container '#{name_or_id}' not found on endpoint #{endpoint_id}. Available:\n#{available}") end def create_exec(endpoint_id, container_id, shell)