diff --git a/pkg/common/accessio/format.go b/pkg/common/accessio/format.go index 09c29dd660..8ccb113184 100644 --- a/pkg/common/accessio/format.go +++ b/pkg/common/accessio/format.go @@ -41,6 +41,7 @@ const ( FormatTar FileFormat = "tar" FormatTGZ FileFormat = "tgz" FormatDirectory FileFormat = "directory" + FormatNone FileFormat = "" ) var suffixes = map[FileFormat]string{ @@ -77,18 +78,24 @@ func GetFormatsFor[T any](fileFormats map[FileFormat]T) []string { return list } -func FileFormatForType(t string) FileFormat { +// FileFormatForTypeSpec returns the format hint provided +// by a type specification.The format hint is an optional +// suffix separated by a +. +func FileFormatForTypeSpec(t string) FileFormat { i := strings.Index(t, "+") if i < 0 { - return FileFormat(t) + return "" } return FileFormat(t[i+1:]) } -func TypeForType(t string) string { +// TypeForTypeSpec returns the pure type info provided +// by a type specification.The format hint is an optional +// suffix separated by a +. +func TypeForTypeSpec(t string) string { i := strings.Index(t, "+") if i < 0 { - return "" + return t } return t[:i] } diff --git a/pkg/common/accessobj/format.go b/pkg/common/accessobj/format.go index 2971a39b38..fd321ccb5d 100644 --- a/pkg/common/accessobj/format.go +++ b/pkg/common/accessobj/format.go @@ -149,3 +149,30 @@ func DefaultCreateOptsFileHandling(kind string, info AccessObjectInfo, path stri return NewAccessObject(info, ACC_CREATE, opts.GetRepresentation(), nil, CloserFunction(func(obj *AccessObject) error { return handler.Write(obj, path, opts, mode) }), DirMode) } + +//////////////////////////////////////////////////////////////////////////////// + +// MapType maps a given type name to an effective type and a format. +func MapType(hint string, efftyp string, deffmt accessio.FileFormat, useFormats bool, alt ...string) (string, accessio.FileFormat) { + typ := accessio.TypeForTypeSpec(hint) + f := accessio.FileFormatForTypeSpec(hint) + if f != "" { + deffmt = f + } + if typ == efftyp { + return efftyp, deffmt + } + for _, t := range alt { + if typ == t { + return efftyp, deffmt + } + } + if useFormats { + for _, f := range accessio.GetFormats() { + if hint == f { + return efftyp, accessio.FileFormat(f) + } + } + } + return "", "" +} diff --git a/pkg/contexts/oci/grammar/grammar.go b/pkg/contexts/oci/grammar/grammar.go index 8280deacd0..e949574f11 100644 --- a/pkg/contexts/oci/grammar/grammar.go +++ b/pkg/contexts/oci/grammar/grammar.go @@ -5,6 +5,8 @@ package grammar import ( + "strings" + . "github.com/open-component-model/ocm/pkg/regex" ) @@ -217,3 +219,11 @@ var ( // identifier value, anchored at start and end of string. AnchoredIdentifierRegexp = Anchored(IdentifierRegexp) ) + +func SplitTypeSpec(t string) (string, string) { + i := strings.Index(t, "+") + if i < 0 { + return t, "" + } + return t[:i], t[i+1:] +} diff --git a/pkg/contexts/oci/ref.go b/pkg/contexts/oci/ref.go index d068c65ba0..24500d5faf 100644 --- a/pkg/contexts/oci/ref.go +++ b/pkg/contexts/oci/ref.go @@ -36,14 +36,20 @@ func ParseRepo(ref string) (UniformRepositorySpec, error) { if match == nil { return UniformRepositorySpec{}, errors.ErrInvalid(KIND_OCI_REFERENCE, ref) } + h := string(match[1]) + t, _ := grammar.SplitTypeSpec(h) return UniformRepositorySpec{ - Type: string(match[1]), + Type: t, + TypeHint: h, Info: string(match[2]), CreateIfMissing: create, }, nil } + h := string(match[1]) + t, _ := grammar.SplitTypeSpec(h) return UniformRepositorySpec{ - Type: string(match[1]), + Type: t, + TypeHint: h, Scheme: string(match[2]), Host: string(match[3]), CreateIfMissing: create, diff --git a/pkg/contexts/oci/ref_test.go b/pkg/contexts/oci/ref_test.go index feaa9b37fc..3dc52f38b2 100644 --- a/pkg/contexts/oci/ref_test.go +++ b/pkg/contexts/oci/ref_test.go @@ -204,8 +204,9 @@ var _ = Describe("ref parsing", func() { Info: "alias", }) CheckRepo("tar::a/b.tar", &oci.UniformRepositorySpec{ - Type: "tar", - Info: "a/b.tar", + Type: "tar", + Info: "a/b.tar", + TypeHint: "tar", }) CheckRepo("a/b.tar", &oci.UniformRepositorySpec{ Info: "a/b.tar", diff --git a/pkg/contexts/oci/repositories/artifactset/uniform.go b/pkg/contexts/oci/repositories/artifactset/uniform.go index 722088b7ef..01aef447f5 100644 --- a/pkg/contexts/oci/repositories/artifactset/uniform.go +++ b/pkg/contexts/oci/repositories/artifactset/uniform.go @@ -15,9 +15,6 @@ func init() { h := &repospechandler{} cpi.RegisterRepositorySpecHandler(h, "") cpi.RegisterRepositorySpecHandler(h, Type) - for _, f := range SupportedFormats() { - cpi.RegisterRepositorySpecHandler(h, string(f)) - } } type repospechandler struct{} @@ -32,14 +29,14 @@ func (h *repospechandler) MapReference(ctx cpi.Context, u *cpi.UniformRepository } fs := vfsattr.Get(ctx) - hint := u.TypeHint + hint, f := accessobj.MapType(u.TypeHint, Type, accessio.FormatDirectory, false) if !u.CreateIfMissing { hint = "" } - create, ok, err := accessobj.CheckFile(Type, hint, accessio.TypeForType(u.Type) == Type, path, fs, ArtifactSetDescriptorFileName) + create, ok, err := accessobj.CheckFile(Type, hint, accessio.TypeForTypeSpec(u.Type) == Type, path, fs, ArtifactSetDescriptorFileName) if err == nil && !ok { - create, ok, err = accessobj.CheckFile(Type, hint, accessio.TypeForType(u.Type) == Type, path, fs, OCIArtifactSetDescriptorFileName) + create, ok, err = accessobj.CheckFile(Type, hint, accessio.TypeForTypeSpec(u.Type) == Type, path, fs, OCIArtifactSetDescriptorFileName) } if !ok || err != nil { @@ -47,8 +44,10 @@ func (h *repospechandler) MapReference(ctx cpi.Context, u *cpi.UniformRepository } mode := accessobj.ACC_WRITABLE + createHint := accessio.FormatNone if create { mode |= accessobj.ACC_CREATE + createHint = f } - return NewRepositorySpec(mode, path, accessio.FileFormatForType(u.Type), accessio.PathFileSystem(fs)) + return NewRepositorySpec(mode, path, createHint, accessio.PathFileSystem(fs)) } diff --git a/pkg/contexts/oci/repositories/ctf/uniform.go b/pkg/contexts/oci/repositories/ctf/uniform.go index a0a9fc4832..aa95a0458a 100644 --- a/pkg/contexts/oci/repositories/ctf/uniform.go +++ b/pkg/contexts/oci/repositories/ctf/uniform.go @@ -11,6 +11,8 @@ import ( "github.com/open-component-model/ocm/pkg/contexts/oci/cpi" ) +const AltType = "ctf" + func init() { h := &repospechandler{} cpi.RegisterRepositorySpecHandler(h, "") @@ -36,12 +38,13 @@ func MapReference(ctx cpi.Context, u *cpi.UniformRepositorySpec) (cpi.Repository } fs := vfsattr.Get(ctx) - hint := u.TypeHint + typ, _ := accessobj.MapType(u.Type, Type, accessio.FormatNone, true, AltType) + hint, f := accessobj.MapType(u.TypeHint, Type, accessio.FormatDirectory, true, AltType) if !u.CreateIfMissing { hint = "" } - create, ok, err := accessobj.CheckFile(Type, hint, accessio.TypeForType(u.Type) != "", path, fs, ArtifactIndexFileName) - if !ok || err != nil { + create, ok, err := accessobj.CheckFile(Type, hint, accessio.TypeForTypeSpec(typ) == Type, path, fs, ArtifactIndexFileName) + if !ok || (err != nil && typ == "") { if err != nil { return nil, err } @@ -50,8 +53,10 @@ func MapReference(ctx cpi.Context, u *cpi.UniformRepositorySpec) (cpi.Repository } } mode := accessobj.ACC_WRITABLE + createHint := accessio.FormatNone if create { mode |= accessobj.ACC_CREATE + createHint = f } - return NewRepositorySpec(mode, path, accessio.FileFormatForType(u.Type), accessio.PathFileSystem(fs)) + return NewRepositorySpec(mode, path, createHint, accessio.PathFileSystem(fs)) } diff --git a/pkg/contexts/ocm/grammar/grammar.go b/pkg/contexts/ocm/grammar/grammar.go index e48b8f2c5c..96aa3ef385 100644 --- a/pkg/contexts/ocm/grammar/grammar.go +++ b/pkg/contexts/ocm/grammar/grammar.go @@ -67,3 +67,7 @@ var ( Optional(Literal(VersionSeparator), Capture(VersionRegexp))), ) ) + +func SplitTypeSpec(t string) (string, string) { + return grammar.SplitTypeSpec(t) +} diff --git a/pkg/contexts/ocm/internal/context.go b/pkg/contexts/ocm/internal/context.go index 13859da388..5e395afad2 100644 --- a/pkg/contexts/ocm/internal/context.go +++ b/pkg/contexts/ocm/internal/context.go @@ -309,6 +309,7 @@ func (c *_context) SetAlias(name string, spec RepositorySpec) { } func (c *_context) GetResolver() ComponentVersionResolver { + c.Update() if len(c.resolver.rules) == 0 { return nil } diff --git a/pkg/contexts/ocm/ref.go b/pkg/contexts/ocm/ref.go index b26bb3144e..366027188f 100644 --- a/pkg/contexts/ocm/ref.go +++ b/pkg/contexts/ocm/ref.go @@ -12,6 +12,7 @@ import ( "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" "github.com/open-component-model/ocm/pkg/contexts/ocm/grammar" "github.com/open-component-model/ocm/pkg/errors" + "github.com/open-component-model/ocm/pkg/utils" ) const ( @@ -37,25 +38,34 @@ func ParseRepo(ref string) (UniformRepositorySpec, error) { if match == nil { return UniformRepositorySpec{}, errors.ErrInvalid(KIND_OCM_REFERENCE, ref) } + h := string(match[1]) + t, _ := grammar.SplitTypeSpec(h) return cpi.HandleRef(UniformRepositorySpec{ - Type: string(match[1]), + Type: t, + TypeHint: h, Info: string(match[2]), CreateIfMissing: create, }) } + h := string(match[1]) + t, _ := grammar.SplitTypeSpec(h) return cpi.HandleRef(UniformRepositorySpec{ - Type: string(match[1]), + Type: t, + TypeHint: h, Host: string(match[2]), SubPath: string(match[3]), CreateIfMissing: create, }) } -func ParseRepoToSpec(ctx Context, ref string) (RepositorySpec, error) { +func ParseRepoToSpec(ctx Context, ref string, create ...bool) (RepositorySpec, error) { uni, err := ParseRepo(ref) if err != nil { return nil, errors.ErrInvalidWrap(err, KIND_REPOSITORYSPEC, ref) } + if !uni.CreateIfMissing { + uni.CreateIfMissing = utils.Optional(create...) + } repoSpec, err := ctx.MapUniformRepositorySpec(&uni) if err != nil { return nil, errors.ErrInvalidWrap(err, KIND_REPOSITORYSPEC, ref) @@ -86,9 +96,12 @@ func ParseRef(ref string) (RefSpec, error) { return RefSpec{}, errors.ErrInvalid(KIND_OCM_REFERENCE, ref) } v = string(match[4]) + h := string(match[1]) + t, _ := grammar.SplitTypeSpec(h) spec = RefSpec{ UniformRepositorySpec{ - Type: string(match[1]), + Type: t, + TypeHint: h, Info: string(match[2]), CreateIfMissing: create, }, @@ -99,9 +112,12 @@ func ParseRef(ref string) (RefSpec, error) { } } else { v = string(match[5]) + h := string(match[1]) + t, _ := grammar.SplitTypeSpec(h) spec = RefSpec{ UniformRepositorySpec{ - Type: string(match[1]), + Type: t, + TypeHint: h, Host: string(match[2]), SubPath: string(match[3]), CreateIfMissing: create, diff --git a/pkg/contexts/ocm/ref_test.go b/pkg/contexts/ocm/ref_test.go index decad5b269..4fad348779 100644 --- a/pkg/contexts/ocm/ref_test.go +++ b/pkg/contexts/ocm/ref_test.go @@ -13,6 +13,7 @@ import ( "github.com/open-component-model/ocm/pkg/contexts/ocm" "github.com/open-component-model/ocm/pkg/contexts/ocm/repositories/ocireg" + "github.com/open-component-model/ocm/pkg/utils" ) func Type(t string) string { @@ -34,11 +35,14 @@ func Vers(t string) string { return ":" + t } -func CheckRef(ref, ut, h, us, c, uv, i string) { +func CheckRef(ref, ut, h, us, c, uv, i string, th ...string) { var v *string if uv != "" { v = &uv } + if len(th) == 0 && ut != "" { + th = []string{ut} + } spec, err := ocm.ParseRef(ref) Expect(err).WithOffset(1).To(Succeed()) Expect(spec).WithOffset(1).To(Equal(ocm.RefSpec{ @@ -47,6 +51,7 @@ func CheckRef(ref, ut, h, us, c, uv, i string) { Host: h, SubPath: us, Info: i, + TypeHint: utils.Optional(th...), CreateIfMissing: ref[0] == '+', }, CompSpec: ocm.CompSpec{ @@ -101,8 +106,8 @@ var _ = Describe("ref parsing", func() { }) It("dir ref", func() { - CheckRef("+ctf+directory::./file//bla.blob/comp", "ctf+directory", "", "", "bla.blob/comp", "", "./file") - CheckRef("ctf+directory::./file//bla.blob/comp", "ctf+directory", "", "", "bla.blob/comp", "", "./file") + CheckRef("+ctf+directory::./file//bla.blob/comp", "ctf", "", "", "bla.blob/comp", "", "./file", "ctf+directory") + CheckRef("ctf+directory::./file//bla.blob/comp", "ctf", "", "", "bla.blob/comp", "", "./file", "ctf+directory") CheckRef("directory::./file//bla.blob/comp", "directory", "", "", "bla.blob/comp", "", "./file") CheckRef("directory::file//bla.blob/comp", "directory", "", "", "bla.blob/comp", "", "file") CheckRef("directory::./file.io//bla.blob/comp", "directory", "", "", "bla.blob/comp", "", "./file.io") @@ -120,7 +125,7 @@ var _ = Describe("ref parsing", func() { SubPath: "dev/v1", Info: "", CreateIfMissing: false, - TypeHint: "", + TypeHint: "OCIRegistry", }, CompSpec: ocm.CompSpec{ Component: "github.wdf.sap.corp/kubernetes/landscape-setup-dependencies", diff --git a/pkg/contexts/ocm/repositories/comparch/uniform.go b/pkg/contexts/ocm/repositories/comparch/uniform.go index f1b611caec..7577571316 100644 --- a/pkg/contexts/ocm/repositories/comparch/uniform.go +++ b/pkg/contexts/ocm/repositories/comparch/uniform.go @@ -8,7 +8,6 @@ import ( "github.com/open-component-model/ocm/pkg/common/accessio" "github.com/open-component-model/ocm/pkg/common/accessobj" "github.com/open-component-model/ocm/pkg/contexts/datacontext/attrs/vfsattr" - "github.com/open-component-model/ocm/pkg/contexts/oci/repositories/ctf" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" ) @@ -17,11 +16,6 @@ func init() { cpi.RegisterRepositorySpecHandler(h, "") cpi.RegisterRepositorySpecHandler(h, Type) cpi.RegisterRepositorySpecHandler(h, "ca") - for _, f := range ctf.SupportedFormats() { - cpi.RegisterRepositorySpecHandler(h, string(f)) - cpi.RegisterRepositorySpecHandler(h, "ca+"+string(f)) - cpi.RegisterRepositorySpecHandler(h, Type+"+"+string(f)) - } } type repospechandler struct{} @@ -35,12 +29,14 @@ func (h *repospechandler) MapReference(ctx cpi.Context, u *cpi.UniformRepository path = u.Host } fs := vfsattr.Get(ctx) - hint := u.TypeHint + + typ, _ := accessobj.MapType(u.Type, Type, accessio.FormatNone, true, "ca") + hint, f := accessobj.MapType(u.TypeHint, Type, accessio.FormatDirectory, false, "ca") if !u.CreateIfMissing { hint = "" } - create, ok, err := accessobj.CheckFile(Type, hint, accessio.TypeForType(u.Type) == Type, path, fs, ComponentDescriptorFileName) - if !ok || err != nil { + create, ok, err := accessobj.CheckFile(Type, hint, accessio.TypeForTypeSpec(typ) == Type, path, fs, ComponentDescriptorFileName) + if !ok || (err != nil && typ == "") { if err != nil { return nil, err } @@ -49,8 +45,10 @@ func (h *repospechandler) MapReference(ctx cpi.Context, u *cpi.UniformRepository } } mode := accessobj.ACC_WRITABLE + createHint := accessio.FormatNone if create { mode |= accessobj.ACC_CREATE + createHint = f } - return NewRepositorySpec(mode, path, accessio.FileFormatForType(u.Type), accessio.PathFileSystem(fs)) + return NewRepositorySpec(mode, path, createHint, accessio.PathFileSystem(fs)) } diff --git a/pkg/contexts/ocm/repositories/ctf/ctf_test.go b/pkg/contexts/ocm/repositories/ctf/ctf_test.go new file mode 100644 index 0000000000..2602c36381 --- /dev/null +++ b/pkg/contexts/ocm/repositories/ctf/ctf_test.go @@ -0,0 +1,90 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package ctf_test + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + . "github.com/open-component-model/ocm/pkg/testutils" + + "github.com/mandelsoft/vfs/pkg/memoryfs" + "github.com/mandelsoft/vfs/pkg/vfs" + + "github.com/open-component-model/ocm/pkg/common/accessio" + "github.com/open-component-model/ocm/pkg/contexts/datacontext" + "github.com/open-component-model/ocm/pkg/contexts/datacontext/attrs/vfsattr" + "github.com/open-component-model/ocm/pkg/contexts/ocm" + "github.com/open-component-model/ocm/pkg/contexts/ocm/repositories/comparch" + "github.com/open-component-model/ocm/pkg/contexts/ocm/repositories/ctf" +) + +var _ = Describe("access method", func() { + var fs vfs.FileSystem + var ctx ocm.Context + + BeforeEach(func() { + ctx = ocm.New(datacontext.MODE_EXTENDED) + fs = memoryfs.New() + vfsattr.Set(ctx.AttributesContext(), fs) + }) + + Context("create", func() { + It("create ctf", func() { + MustBeSuccessful(fs.Mkdir("test", 0o700)) + + spec := Must(ocm.ParseRepoToSpec(ctx, "+ctf::test/repository")) + Expect(ctf.NewRepositorySpec(ctf.ACC_CREATE, "test/repository", accessio.FormatDirectory, accessio.PathFileSystem(fs))).To(DeepEqual(spec)) + }) + + It("create directory", func() { + MustBeSuccessful(fs.Mkdir("test", 0o700)) + + spec := Must(ocm.ParseRepoToSpec(ctx, "+directory::test/repository")) + Expect(ctf.NewRepositorySpec(ctf.ACC_CREATE, "test/repository", accessio.FormatDirectory, accessio.PathFileSystem(fs))).To(DeepEqual(spec)) + }) + + It("create tgz", func() { + MustBeSuccessful(fs.Mkdir("test", 0o700)) + + spec := Must(ocm.ParseRepoToSpec(ctx, "+tgz::test/repository")) + Expect(ctf.NewRepositorySpec(ctf.ACC_CREATE, "test/repository", accessio.FormatTGZ, accessio.PathFileSystem(fs))).To(DeepEqual(spec)) + }) + + It("create ca", func() { + MustBeSuccessful(fs.Mkdir("test", 0o700)) + + spec := Must(ocm.ParseRepoToSpec(ctx, "+ca::test/repository")) + Expect(comparch.NewRepositorySpec(ctf.ACC_CREATE, "test/repository", accessio.FormatDirectory, accessio.PathFileSystem(fs))).To(DeepEqual(spec)) + }) + }) + + Context("read", func() { + It("read ctf", func() { + ExpectError(ocm.ParseRepoToSpec(ctx, "test/repository")).To(MatchError(`repository specification "test/repository" is invalid: repository "test/repository" is unknown`)) + }) + + It("read ctf", func() { + MustBeSuccessful(fs.Mkdir("test", 0o700)) + + spec := Must(ocm.ParseRepoToSpec(ctx, "ctf::test/repository")) + Expect(ctf.NewRepositorySpec(ctf.ACC_WRITABLE, "test/repository", accessio.PathFileSystem(fs))).To(DeepEqual(spec)) + }) + + It("read ctf", func() { + MustBeSuccessful(fs.Mkdir("test", 0o700)) + + spec := Must(ocm.ParseRepoToSpec(ctx, "tgz::test/repository")) + Expect(ctf.NewRepositorySpec(ctf.ACC_WRITABLE, "test/repository", accessio.PathFileSystem(fs))).To(DeepEqual(spec)) + }) + + It("read ca", func() { + MustBeSuccessful(fs.Mkdir("test", 0o700)) + + spec := Must(ocm.ParseRepoToSpec(ctx, "ca::test/repository")) + Expect(comparch.NewRepositorySpec(ctf.ACC_WRITABLE, "test/repository", accessio.PathFileSystem(fs))).To(DeepEqual(spec)) + }) + + }) +}) diff --git a/pkg/contexts/ocm/repositories/ctf/type.go b/pkg/contexts/ocm/repositories/ctf/type.go index 9a69dabae9..d9ee6cbdd8 100644 --- a/pkg/contexts/ocm/repositories/ctf/type.go +++ b/pkg/contexts/ocm/repositories/ctf/type.go @@ -11,6 +11,8 @@ import ( "github.com/open-component-model/ocm/pkg/contexts/ocm/repositories/genericocireg" ) +const Type = ctf.Type + func NewRepositorySpec(acc accessobj.AccessMode, path string, opts ...accessio.Option) (*genericocireg.RepositorySpec, error) { spec, err := ctf.NewRepositorySpec(acc, path, opts...) if err != nil { diff --git a/pkg/contexts/ocm/repositories/ctf/uniform.go b/pkg/contexts/ocm/repositories/ctf/uniform.go index 52f47228c0..616aa03780 100644 --- a/pkg/contexts/ocm/repositories/ctf/uniform.go +++ b/pkg/contexts/ocm/repositories/ctf/uniform.go @@ -23,8 +23,6 @@ func init() { cpi.RegisterRepositorySpecHandler(h, "ctf") for _, f := range SupportedFormats() { cpi.RegisterRepositorySpecHandler(h, string(f)) - cpi.RegisterRepositorySpecHandler(h, "ctf+"+string(f)) - cpi.RegisterRepositorySpecHandler(h, ctf.Type+"+"+string(f)) } } diff --git a/pkg/contexts/ocm/utils.go b/pkg/contexts/ocm/utils.go index ed14105232..1518dbab84 100644 --- a/pkg/contexts/ocm/utils.go +++ b/pkg/contexts/ocm/utils.go @@ -7,6 +7,7 @@ package ocm import ( "fmt" "io" + "strings" "github.com/mandelsoft/vfs/pkg/vfs" @@ -48,17 +49,16 @@ func AssureTargetRepository(session Session, ctx Context, targetref string, opts if err != nil { return nil, err } - if ref.Type != "" { - format = accessio.FileFormat(ref.Type) + if ref.TypeHint == "" { + ref.TypeHint = archive } - if archive != "" && format != "" { + if format != "" && ref.TypeHint != "" && !strings.Contains(ref.TypeHint, "+") { for _, f := range ctf.SupportedFormats() { if f == format { - ref.Type = archive + "+" + format.String() + ref.TypeHint += "+" + format.String() } } } - ref.TypeHint = archive ref.CreateIfMissing = true target, err := session.DetermineRepositoryBySpec(ctx, &ref) if err != nil {