From 55b88882d765a2abe78c80fb5a8a3e8e0b9ffa42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= Date: Wed, 3 Jun 2026 16:52:55 +0200 Subject: [PATCH 1/2] cli/file_store: Clarify ConvertToHostname MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The implementation seems to have diverged already. Signed-off-by: Paweł Gronowski --- cli/config/credentials/file_store.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/config/credentials/file_store.go b/cli/config/credentials/file_store.go index e3ef8e25edf9..40e1ebcd516d 100644 --- a/cli/config/credentials/file_store.go +++ b/cli/config/credentials/file_store.go @@ -104,7 +104,7 @@ func (c *fileStore) Store(authConfig types.AuthConfig) error { // stored as hostname or as hostname including scheme (in legacy configuration // files). // -// It's the equivalent to [registry.ConvertToHostname] in the daemon. +// It's based on [registry.ConvertToHostname] from Moby daemon. // // [registry.ConvertToHostname]: https://pkg.go.dev/github.com/moby/moby/v2@v2.0.0-beta.7/daemon/pkg/registry#ConvertToHostname func ConvertToHostname(maybeURL string) string { From 3cc61496db33ab4bdd6326ba749f787fb599125f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= Date: Wed, 3 Jun 2026 15:49:22 +0200 Subject: [PATCH 2/2] cli/file_store: Preserve IPv6 URL normalization after Go change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a fallback for unbracketed IPv6 literals to preserve behavior. Signed-off-by: Paweł Gronowski --- cli/config/credentials/file_store.go | 41 ++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/cli/config/credentials/file_store.go b/cli/config/credentials/file_store.go index 40e1ebcd516d..d4037b7a488c 100644 --- a/cli/config/credentials/file_store.go +++ b/cli/config/credentials/file_store.go @@ -117,7 +117,48 @@ func ConvertToHostname(maybeURL string) string { } return net.JoinHostPort(u.Hostname(), u.Port()) } + + if hostName := hostFromURLFallback(stripped); hostName != "" { + return hostName + } } hostName, _, _ := strings.Cut(stripped, "/") return hostName } + +// hostFromURLFallback extracts a host from scheme URLs that net/url rejects. +// Go rejects unbracketed IPv6 literals in URL hosts since +// https://github.com/golang/go/commit/0c28789bd7dfc55099cac86a3212dda0d6c091f6 +func hostFromURLFallback(maybeURL string) string { + _, rest, ok := strings.Cut(maybeURL, "://") + if !ok { + return "" + } + + hostName, _, _ := strings.Cut(rest, "/") + if hostName == "" { + return "" + } + + if strings.Count(hostName, ":") > 1 && !strings.HasPrefix(hostName, "[") { + portStart := strings.LastIndex(hostName, ":") + addr, port := hostName[:portStart], hostName[portStart+1:] + if addr != "" && isPort(port) { + return net.JoinHostPort(addr, port) + } + } + + return hostName +} + +func isPort(port string) bool { + if port == "" { + return false + } + for _, r := range port { + if r < '0' || r > '9' { + return false + } + } + return true +}