From a181ce0035eb2c5b69deb64ac7caa3959a9c5119 Mon Sep 17 00:00:00 2001 From: agammann <161159040+agammann@users.noreply.github.com> Date: Mon, 19 Oct 2026 21:24:28 -0700 Subject: [PATCH] Handle registry ports in image references --- cmd/bashbrew/cmd-children.go | 13 ++++++++++++- cmd/bashbrew/cmd-children_test.go | 26 ++++++++++++++++++++++++++ pkg/dockerfile/parse.go | 5 ++++- pkg/dockerfile/parse_test.go | 21 +++++++++++++++++++++ 4 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 cmd/bashbrew/cmd-children_test.go diff --git a/cmd/bashbrew/cmd-children.go b/cmd/bashbrew/cmd-children.go index 2da21492..1d639456 100644 --- a/cmd/bashbrew/cmd-children.go +++ b/cmd/bashbrew/cmd-children.go @@ -102,7 +102,7 @@ func cmdChildren(c *cli.Context) error { arches.add(from, entryArch) } children.add(from, tag) - if fromRepo, _, ok := strings.Cut(from, ":"); ok { + if fromRepo, ok := repositoryFromImageReference(from); ok { // make sure things like old "alpine" tags that are no longer supported still come up with "bashbrew children alpine" repoTags.add(fromRepo, from) } @@ -178,3 +178,14 @@ func cmdChildren(c *cli.Context) error { return nil } + +func repositoryFromImageReference(reference string) (string, bool) { + repository := reference + if digest := strings.IndexByte(repository, '@'); digest >= 0 { + repository = repository[:digest] + } + if tag := strings.LastIndexByte(repository, ':'); tag > strings.LastIndexByte(repository, '/') { + repository = repository[:tag] + } + return repository, repository != reference +} diff --git a/cmd/bashbrew/cmd-children_test.go b/cmd/bashbrew/cmd-children_test.go new file mode 100644 index 00000000..394d46e0 --- /dev/null +++ b/cmd/bashbrew/cmd-children_test.go @@ -0,0 +1,26 @@ +package main + +import "testing" + +func TestRepositoryFromImageReference(t *testing.T) { + tests := []struct { + reference string + repository string + ok bool + }{ + {"alpine:3.20", "alpine", true}, + {"registry.example:5000/team/base:latest", "registry.example:5000/team/base", true}, + {"registry.example:5000/team/base@sha256:123456", "registry.example:5000/team/base", true}, + {"registry.example:5000/team/base:3@sha256:123456", "registry.example:5000/team/base", true}, + {"scratch", "scratch", false}, + } + + for _, test := range tests { + t.Run(test.reference, func(t *testing.T) { + repository, ok := repositoryFromImageReference(test.reference) + if repository != test.repository || ok != test.ok { + t.Fatalf("repositoryFromImageReference(%q) = %q, %t; want %q, %t", test.reference, repository, ok, test.repository, test.ok) + } + }) + } +} diff --git a/pkg/dockerfile/parse.go b/pkg/dockerfile/parse.go index cba566c8..6eed460b 100644 --- a/pkg/dockerfile/parse.go +++ b/pkg/dockerfile/parse.go @@ -174,7 +174,10 @@ func ParseReader(dockerfile io.Reader) (Metadata, error) { } func latestizeRepoTag(repoTag string) string { - if repoTag != "scratch" && strings.IndexRune(repoTag, ':') < 0 { + if repoTag == "scratch" || strings.Contains(repoTag, "@") { + return repoTag + } + if strings.LastIndexByte(repoTag, ':') <= strings.LastIndexByte(repoTag, '/') { return repoTag + ":latest" } return repoTag diff --git a/pkg/dockerfile/parse_test.go b/pkg/dockerfile/parse_test.go index ddda9711..14a9b815 100644 --- a/pkg/dockerfile/parse_test.go +++ b/pkg/dockerfile/parse_test.go @@ -31,6 +31,27 @@ func TestParse(t *testing.T) { Froms: []string{"bash:5"}, }, }, + { + name: "untagged image from registry with port", + dockerfile: `FROM registry.example:5000/team/base`, + metadata: dockerfile.Metadata{ + Froms: []string{"registry.example:5000/team/base:latest"}, + }, + }, + { + name: "tagged image from registry with port", + dockerfile: `FROM registry.example:5000/team/base:3`, + metadata: dockerfile.Metadata{ + Froms: []string{"registry.example:5000/team/base:3"}, + }, + }, + { + name: "digest image from registry with port", + dockerfile: `FROM registry.example:5000/team/base@sha256:123456`, + metadata: dockerfile.Metadata{ + Froms: []string{"registry.example:5000/team/base@sha256:123456"}, + }, + }, { name: "comments+whitespace+continuation", dockerfile: `