Skip to content
This repository was archived by the owner on Jul 18, 2025. It is now read-only.

Commit 4feb664

Browse files
committed
Use distribution library to parse references
1 parent 7e87ad4 commit 4feb664

File tree

3 files changed

+132
-10
lines changed

3 files changed

+132
-10
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ require (
1010
github.com/atomist-skills/go-skill v0.0.6-0.20221221214636-a7de163fd901
1111
github.com/briandowns/spinner v1.12.0
1212
github.com/docker/cli v20.10.21+incompatible
13+
github.com/docker/distribution v2.8.1+incompatible
1314
github.com/docker/docker v20.10.17+incompatible
1415
github.com/dustin/go-humanize v1.0.0
1516
github.com/google/go-containerregistry v0.11.0
@@ -71,7 +72,6 @@ require (
7172
github.com/dgryski/go-minhash v0.0.0-20170608043002-7fe510aff544 // indirect
7273
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
7374
github.com/dimchansky/utfbom v1.1.1 // indirect
74-
github.com/docker/distribution v2.8.1+incompatible // indirect
7575
github.com/docker/docker-credential-helpers v0.6.4 // indirect
7676
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect
7777
github.com/docker/go-connections v0.4.0 // indirect

registry/save.go

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ import (
2626

2727
stereoscopeimage "github.com/anchore/stereoscope/pkg/image"
2828
"github.com/anchore/syft/syft/source"
29+
"github.com/atomist-skills/go-skill"
30+
"github.com/docker/cli/cli/command"
31+
"github.com/docker/distribution/reference"
32+
"github.com/docker/index-cli-plugin/internal"
2933
"github.com/dustin/go-humanize"
3034
"github.com/google/go-containerregistry/pkg/authn"
3135
"github.com/google/go-containerregistry/pkg/name"
@@ -37,11 +41,6 @@ import (
3741
"github.com/google/go-containerregistry/pkg/v1/tarball"
3842
"github.com/google/uuid"
3943
"github.com/pkg/errors"
40-
41-
"github.com/atomist-skills/go-skill"
42-
43-
"github.com/docker/cli/cli/command"
44-
"github.com/docker/index-cli-plugin/internal"
4544
)
4645

4746
type ImageId struct {
@@ -265,12 +264,12 @@ func SaveImage(image string, username string, password string, cli command.Cli)
265264
var name, digest string
266265
tags := make([]string, 0)
267266
for _, d := range im.RepoDigests {
268-
name = strings.Split(d, "@")[0]
269-
digest = strings.Split(d, "@")[1]
267+
name, digest = mustParseNameAndDigest(d)
270268
}
271269
for _, t := range im.RepoTags {
272-
name = strings.Split(t, ":")[0]
273-
tags = append(tags, strings.Split(t, ":")[1])
270+
var tag string
271+
name, tag = mustParseNameAndTag(t)
272+
tags = append(tags, tag)
274273
}
275274

276275
return &ImageCache{
@@ -356,3 +355,27 @@ func WithAuth(username string, password string) remote.Option {
356355
}
357356
return remote.WithAuthFromKeychain(authn.DefaultKeychain)
358357
}
358+
359+
func mustParseNameAndTag(imageRef string) (string, string) {
360+
parsed, err := reference.Parse(imageRef)
361+
if err != nil {
362+
panic("expected imageRef to be a NamedTagged reference")
363+
}
364+
tagged, ok := parsed.(reference.NamedTagged)
365+
if !ok {
366+
panic("expected imageRef to be a NamedTagged reference")
367+
}
368+
return tagged.Name(), tagged.Tag()
369+
}
370+
371+
func mustParseNameAndDigest(imageRef string) (string, string) {
372+
parsed, err := reference.Parse(imageRef)
373+
if err != nil {
374+
panic("expected imageRef to be a Canonical reference")
375+
}
376+
canonical, ok := parsed.(reference.Canonical)
377+
if !ok {
378+
panic("expected imageRef to be a Canonical reference")
379+
}
380+
return canonical.Name(), canonical.Digest().String()
381+
}

registry/save_test.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package registry
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func Test_mustParseNameAndTag(t *testing.T) {
8+
type testCase struct {
9+
description string
10+
input string
11+
name string
12+
tag string
13+
}
14+
testCases := []testCase{
15+
{
16+
description: "library namespace",
17+
input: "foo:1",
18+
name: "foo",
19+
tag: "1",
20+
},
21+
{
22+
description: "namespace",
23+
input: "foo/bar:2",
24+
name: "foo/bar",
25+
tag: "2",
26+
},
27+
{
28+
description: "registry",
29+
input: "registry.example.com/foo/bar:3",
30+
name: "registry.example.com/foo/bar",
31+
tag: "3",
32+
},
33+
{
34+
description: "registry with port",
35+
input: "localhost:8082/foo/bar:4",
36+
name: "localhost:8082/foo/bar",
37+
tag: "4",
38+
},
39+
}
40+
41+
for _, tc := range testCases {
42+
t.Run(tc.description, func(t *testing.T) {
43+
name, tag := mustParseNameAndTag(tc.input)
44+
if name != tc.name {
45+
t.Errorf("expected name to be '%s', got '%s'", tc.name, name)
46+
}
47+
if tag != tc.tag {
48+
t.Errorf("expected tag to be '%s', got '%s'", tc.tag, tag)
49+
}
50+
})
51+
}
52+
}
53+
54+
func Test_mustParseNameAndDigest(t *testing.T) {
55+
type testCase struct {
56+
description string
57+
input string
58+
name string
59+
digest string
60+
}
61+
testCases := []testCase{
62+
{
63+
description: "library namespace",
64+
input: "foo@sha256:544e165df59f0effc3b4aa054712710e0a0913c050d524c44772539c515d1a43",
65+
name: "foo",
66+
digest: "sha256:544e165df59f0effc3b4aa054712710e0a0913c050d524c44772539c515d1a43",
67+
},
68+
{
69+
description: "namespace",
70+
input: "foo/bar@sha256:00b8c9532fbc7894ef92b07322f1943ce23a4935ea1dd4e1a04297831d3aad45",
71+
name: "foo/bar",
72+
digest: "sha256:00b8c9532fbc7894ef92b07322f1943ce23a4935ea1dd4e1a04297831d3aad45",
73+
},
74+
{
75+
description: "registry",
76+
input: "registry.example.com/foo/bar@sha256:57a2a04950c1bd45958947a1b5414558bc2bee863fee519c45da34b398d6d29e",
77+
name: "registry.example.com/foo/bar",
78+
digest: "sha256:57a2a04950c1bd45958947a1b5414558bc2bee863fee519c45da34b398d6d29e",
79+
},
80+
{
81+
description: "registry with port",
82+
input: "localhost:8082/foo/bar@sha256:2adb2c6ade4b433326ea8cddd5c5aa9998d07e8ff603374e3360290807d8c14b",
83+
name: "localhost:8082/foo/bar",
84+
digest: "sha256:2adb2c6ade4b433326ea8cddd5c5aa9998d07e8ff603374e3360290807d8c14b",
85+
},
86+
}
87+
88+
for _, tc := range testCases {
89+
t.Run(tc.description, func(t *testing.T) {
90+
name, digest := mustParseNameAndDigest(tc.input)
91+
if name != tc.name {
92+
t.Errorf("expected name to be '%s', got '%s'", tc.name, name)
93+
}
94+
if digest != tc.digest {
95+
t.Errorf("expected digest to be '%s', got '%s'", tc.digest, digest)
96+
}
97+
})
98+
}
99+
}

0 commit comments

Comments
 (0)