Fuzzy container name matching in console command

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 <noreply@anthropic.com>
This commit is contained in:
2026-03-22 13:03:09 +01:00
parent d8e00a4f1a
commit 855877f6a6
+25 -7
View File
@@ -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 "<stack>-<name>-<n>" 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)