diff --git a/cmds/ocm/commands/ocmcmds/common/inputs/options/standard.go b/cmds/ocm/commands/ocmcmds/common/inputs/options/standard.go index 0ba2712520..d87863f365 100644 --- a/cmds/ocm/commands/ocmcmds/common/inputs/options/standard.go +++ b/cmds/ocm/commands/ocmcmds/common/inputs/options/standard.go @@ -1,7 +1,3 @@ -// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Open Component Model contributors. -// -// SPDX-License-Identifier: Apache-2.0 - package options import ( @@ -14,41 +10,45 @@ var ( MediaTypeOption = options.MediatypeOption ) -var PathOption = flagsets.NewStringOptionType("inputPath", "path field for input") - +// string options var ( - CompressOption = flagsets.NewBoolOptionType("inputCompress", "compress option for input") - ExcludeOption = flagsets.NewStringArrayOptionType("inputExcludes", "excludes (path) for inputs") + VersionOption = flagsets.NewStringOptionType("inputVersion", "version info for inputs") + TextOption = flagsets.NewStringOptionType("inputText", "utf8 text") + HelmRepositoryOption = flagsets.NewStringOptionType("inputHelmRepository", "helm repository base URL") +) +var ( + VariantsOption = flagsets.NewStringArrayOptionType("inputVariants", "(platform) variants for inputs") + PlatformsOption = flagsets.NewStringArrayOptionType("inputPlatforms", "input filter for image platforms ([os]/[architecture])") ) +// path options var ( - IncludeOption = flagsets.NewStringArrayOptionType("inputIncludes", "includes (path) for inputs") - PreserveDirOption = flagsets.NewBoolOptionType("inputPreserveDir", "preserve directory in archive for inputs") + PathOption = flagsets.NewPathOptionType("inputPath", "path field for input") +) +var ( + IncludeOption = flagsets.NewPathArrayOptionType("inputIncludes", "includes (path) for inputs") + ExcludeOption = flagsets.NewPathArrayOptionType("inputExcludes", "excludes (path) for inputs") + LibrariesOption = flagsets.NewPathArrayOptionType("inputLibraries", "library path for inputs") ) +// boolean options var ( + CompressOption = flagsets.NewBoolOptionType("inputCompress", "compress option for input") + PreserveDirOption = flagsets.NewBoolOptionType("inputPreserveDir", "preserve directory in archive for inputs") FollowSymlinksOption = flagsets.NewBoolOptionType("inputFollowSymlinks", "follow symbolic links during archive creation for inputs") - VariantsOption = flagsets.NewStringArrayOptionType("inputVariants", "(platform) variants for inputs") ) -var LibrariesOption = flagsets.NewStringArrayOptionType("inputLibraries", "library path for inputs") - -var VersionOption = flagsets.NewStringOptionType("inputVersion", "version info for inputs") - -var ValuesOption = flagsets.NewValueMapYAMLOptionType("inputValues", "YAML based generic values for inputs") - -var DataOption = flagsets.NewBytesOptionType("inputData", "data (string, !!string or !") - -var TextOption = flagsets.NewStringOptionType("inputText", "utf8 text") - -var YAMLOption = flagsets.NewYAMLOptionType("inputYaml", "YAML formatted text") - -var JSONOption = flagsets.NewYAMLOptionType("inputJson", "JSON formatted text") - -var FormattedJSONOption = flagsets.NewYAMLOptionType("inputFormattedJson", "JSON formatted text") - -var HelmRepositoryOption = flagsets.NewStringOptionType("inputHelmRepository", "helm repository base URL") +// data options +var ( + DataOption = flagsets.NewBytesOptionType("inputData", "data (string, !!string or !") +) +// yaml/json options var ( - PlatformsOption = flagsets.NewStringArrayOptionType("inputPlatforms", "input filter for image platforms ([os]/[architecture])") + YAMLOption = flagsets.NewYAMLOptionType("inputYaml", "YAML formatted text") + JSONOption = flagsets.NewYAMLOptionType("inputJson", "JSON formatted text") + FormattedJSONOption = flagsets.NewYAMLOptionType("inputFormattedJson", "JSON formatted text") +) +var ( + ValuesOption = flagsets.NewValueMapYAMLOptionType("inputValues", "YAML based generic values for inputs") ) diff --git a/cmds/ocm/commands/ocmcmds/common/inputs/utils.go b/cmds/ocm/commands/ocmcmds/common/inputs/utils.go index 18918072cd..973b4424ba 100644 --- a/cmds/ocm/commands/ocmcmds/common/inputs/utils.go +++ b/cmds/ocm/commands/ocmcmds/common/inputs/utils.go @@ -1,15 +1,11 @@ -// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Open Component Model contributors. -// -// SPDX-License-Identifier: Apache-2.0 - package inputs import ( "fmt" "os" - "github.com/mandelsoft/filepath/pkg/filepath" "github.com/mandelsoft/vfs/pkg/vfs" + "github.com/open-component-model/ocm/pkg/contexts/clictx" "github.com/open-component-model/ocm/pkg/errors" ) @@ -40,7 +36,7 @@ func GetBaseDir(fs vfs.FileSystem, filePath string) (string, error) { return "", fmt.Errorf("unable to read current working directory: %w", err) } } else { - wd = filepath.Dir(filePath) + wd = vfs.Dir(fs, filePath) } return wd, nil } @@ -50,7 +46,7 @@ func GetPath(ctx clictx.Context, path string, inputFilePath string) (string, err if path == "" { return "", fmt.Errorf("path attribute required") } - if filepath.IsAbs(path) { + if vfs.IsAbs(fs, path) { return path, nil } else { wd, err := GetBaseDir(fs, inputFilePath) @@ -58,6 +54,6 @@ func GetPath(ctx clictx.Context, path string, inputFilePath string) (string, err return "", err } - return filepath.Join(wd, path), nil + return vfs.Join(fs, wd, path), nil } } diff --git a/go.mod b/go.mod index c5ab93ab11..484d4235ca 100644 --- a/go.mod +++ b/go.mod @@ -37,10 +37,10 @@ require ( github.com/imdario/mergo v0.3.16 github.com/klauspost/compress v1.17.2 github.com/klauspost/pgzip v1.2.6 - github.com/mandelsoft/filepath v0.0.0-20230412200429-36b1eb66bd27 + github.com/mandelsoft/filepath v0.0.0-20240223090642-3e2777258aa3 github.com/mandelsoft/logging v0.0.0-20230905123808-7042ee3aae45 github.com/mandelsoft/spiff v1.7.0-beta-5 - github.com/mandelsoft/vfs v0.4.1 + github.com/mandelsoft/vfs v0.4.3 github.com/marstr/guid v1.1.0 github.com/mitchellh/copystructure v1.2.0 github.com/mittwald/go-helm-client v0.12.3 diff --git a/go.sum b/go.sum index 8a6fc4cf96..1500917d6b 100644 --- a/go.sum +++ b/go.sum @@ -1826,14 +1826,14 @@ github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7 github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mandelsoft/filepath v0.0.0-20230412200429-36b1eb66bd27 h1:VivN6K8H4H0G4bUmeQH68fQIROL5c8S2iyvVe4teufc= -github.com/mandelsoft/filepath v0.0.0-20230412200429-36b1eb66bd27/go.mod h1:LxhqC7khDoRENwooP6f/vWvia9ivj6TqLYrR39zqkN0= +github.com/mandelsoft/filepath v0.0.0-20240223090642-3e2777258aa3 h1:oo9nIgnyiBgYPbcZslRT4y29siuL5EoNJ/t1tr0xEVQ= +github.com/mandelsoft/filepath v0.0.0-20240223090642-3e2777258aa3/go.mod h1:LxhqC7khDoRENwooP6f/vWvia9ivj6TqLYrR39zqkN0= github.com/mandelsoft/logging v0.0.0-20230905123808-7042ee3aae45 h1:BGJBqw9q1Brn3XAYcj52hhonjV7aHUftVJ3SuJpXQ/M= github.com/mandelsoft/logging v0.0.0-20230905123808-7042ee3aae45/go.mod h1:J/kRqdfAOQmMPfJeAcV2pfX1QLV9/NlAQdAJzpnaU+g= github.com/mandelsoft/spiff v1.7.0-beta-5 h1:3kC10nTviDQhL8diSxp7i4IC2iSiDg6KPbH1CAq7Lfw= github.com/mandelsoft/spiff v1.7.0-beta-5/go.mod h1:TwEeOPuRZxlzQBCLEyVTlHmBSruSGGNdiQ2fovVJ8ao= -github.com/mandelsoft/vfs v0.4.1 h1:HvCC5wySpPIl7K3GFOU5MOftiaXbBlOSa7v+MEzln2g= -github.com/mandelsoft/vfs v0.4.1/go.mod h1:k83vb5I4cqRGJh3TUUVbf2oTF8FrYhvixQ+FwIAgP1Y= +github.com/mandelsoft/vfs v0.4.3 h1:2UMrxQkMXkcHyuqSFhgFDupQ1fmqpKLZuu04DOHx1PA= +github.com/mandelsoft/vfs v0.4.3/go.mod h1:zmbhx2ueQc96buqNXg2S88McBMm2mNFNeyGSpSebrHw= github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= github.com/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI= github.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc= diff --git a/pkg/cobrautils/flag/path.go b/pkg/cobrautils/flag/path.go new file mode 100644 index 0000000000..5dac065003 --- /dev/null +++ b/pkg/cobrautils/flag/path.go @@ -0,0 +1,64 @@ +package flag + +import ( + "strings" + + "github.com/mandelsoft/filepath/pkg/filepath" + "github.com/spf13/pflag" +) + +type pathValue string + +func newPathValue(val string, p *string) *pathValue { + *p = pathConv(val) + return (*pathValue)(p) +} + +func (s *pathValue) Set(val string) error { + *s = pathValue(pathConv(val)) + return nil +} + +func (s *pathValue) Type() string { return "filepath" } + +func (s *pathValue) String() string { return string(*s) } + +func pathConv(sval string) string { + vol, paths, rooted := filepath.SplitPath(sval) + if rooted { + return vol + "/" + strings.Join(paths, "/") + } + return vol + strings.Join(paths, "/") +} + +// PathVar defines a filepath flag with specified name, default value, and usage string. +// The argument p points to a string variable in which to store the value of the flag. +func PathVar(f *pflag.FlagSet, p *string, name string, value string, usage string) { + f.VarP(newPathValue(value, p), name, "", usage) +} + +// PathVarP is like PathVar, but accepts a shorthand letter that can be used after a single dash. +func PathVarP(f *pflag.FlagSet, p *string, name, shorthand string, value string, usage string) { + f.VarP(newPathValue(value, p), name, shorthand, usage) +} + +// Path defines a filepath flag with specified name, default value, and usage string. +// The return value is the address of a string variable that stores the value of the flag. +func Path(f *pflag.FlagSet, name string, value string, usage string) *string { + p := new(string) + PathVarP(f, p, name, "", value, usage) + return p +} + +// PathP is like Path, but accepts a shorthand letter that can be used after a single dash. +func PathP(f *pflag.FlagSet, name, shorthand string, value string, usage string) *string { + p := new(string) + PathVarP(f, p, name, shorthand, value, usage) + return p +} + +// PathVarPF is like PathVarP, but returns the created flag. +func PathVarPF(f *pflag.FlagSet, p *string, name, shorthand string, value string, usage string) *pflag.Flag { + PathVarP(f, p, name, shorthand, value, usage) + return f.Lookup(name) +} diff --git a/pkg/cobrautils/flag/path_array.go b/pkg/cobrautils/flag/path_array.go new file mode 100644 index 0000000000..8aaaad3870 --- /dev/null +++ b/pkg/cobrautils/flag/path_array.go @@ -0,0 +1,112 @@ +package flag + +import ( + "path/filepath" + "strings" + + "github.com/spf13/pflag" +) + +type pathArrayValue struct { + value *[]string + changed bool +} + +func newPathArrayValue(val []string, p *[]string) *pathArrayValue { + ssv := new(pathArrayValue) + ssv.value = p + *ssv.value = pathArrayConv(val) + return ssv +} + +func (s *pathArrayValue) Set(val string) error { + if !s.changed { + *s.value = pathStringListConv(val) + s.changed = true + } else { + *s.value = append(*s.value, pathConv(val)) + } + return nil +} + +func (s *pathArrayValue) Append(val string) error { + *s.value = append(*s.value, pathConv(val)) + return nil +} + +func (s *pathArrayValue) Replace(val []string) error { + out := make([]string, len(val)) + for i, d := range val { + var err error + out[i] = pathConv(d) + if err != nil { + return err + } + } + *s.value = out + return nil +} + +func (s *pathArrayValue) GetSlice() []string { + out := make([]string, len(*s.value)) + copy(out, *s.value) + return out +} + +func (s *pathArrayValue) Type() string { + return "stringArray" +} + +func (s *pathArrayValue) String() string { + str := new(string) + *str = strings.Join(*s.value, string(filepath.ListSeparator)) + return *str +} + +// Converts every string into correct filepath format. See pathConv for more details. +func pathArrayConv(sval []string) []string { + for i, val := range sval { + sval[i] = pathConv(val) + } + return sval +} + +// pathStringListConv converts a string containing multiple filepaths separated by filepath.ListSeparator into a list +// of filepaths. +func pathStringListConv(sval string) []string { + values := filepath.SplitList(sval) + values = pathArrayConv(values) + return values +} + +// PathArrayVar defines a filepath flag with specified name, default value, and usage string. +// The argument p points to a []string variable in which to store the values of the multiple flags. +func PathArrayVar(f *pflag.FlagSet, p *[]string, name string, value []string, usage string) { + f.VarP(newPathArrayValue(value, p), name, "", usage) +} + +// PathArrayVarP is like PathArrayVar, but accepts a shorthand letter that can be used after a single dash. +func PathArrayVarP(f *pflag.FlagSet, p *[]string, name, shorthand string, value []string, usage string) { + f.VarP(newPathArrayValue(value, p), name, shorthand, usage) +} + +// PathArray defines a filepath flag with specified name, default value, and usage string. +// The return value is the address of a []string variable that stores the value of the flag. +func PathArray(f *pflag.FlagSet, name string, value []string, usage string) *[]string { + p := []string{} + PathArrayVarP(f, &p, name, "", value, usage) + return &p +} + +// PathArrayP is like PathArray, but accepts a shorthand letter that can be used after a single dash. +func PathArrayP(f *pflag.FlagSet, name, shorthand string, value []string, usage string) *[]string { + p := []string{} + PathArrayVarP(f, &p, name, shorthand, value, usage) + return &p +} + +// PathArrayVarPF is like PathArrayVarP, but returns the created flag. +func PathArrayVarPF(f *pflag.FlagSet, p *[]string, name, shorthand string, value []string, usage string) *pflag.Flag { + PathArrayVarP(f, p, name, shorthand, value, usage) + return f.Lookup(name) +} diff --git a/pkg/cobrautils/flag/path_array_test.go b/pkg/cobrautils/flag/path_array_test.go new file mode 100644 index 0000000000..4c34d18f02 --- /dev/null +++ b/pkg/cobrautils/flag/path_array_test.go @@ -0,0 +1,34 @@ +//go:build !windows +// +build !windows + +package flag_test + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/spf13/pflag" + + . "github.com/open-component-model/ocm/pkg/cobrautils/flag" +) + +var _ = Describe("path flags", func() { + var flags *pflag.FlagSet + + BeforeEach(func() { + flags = pflag.NewFlagSet("test", pflag.ContinueOnError) + }) + + It("parse windows path", func() { + var val []string + PathArrayVarPF(flags, &val, "path", "p", nil, "help message") + flags.Parse([]string{"-p", `/foo/bar:other/path`}) + Expect(val).To(Equal([]string{"/foo/bar", "other/path"})) + }) + + It("parse default path", func() { + var val []string + PathArrayVarPF(flags, &val, "path", "p", []string{`/foo/bar`, `other/path`}, "help message") + Expect(val).To(Equal([]string{"/foo/bar", "other/path"})) + }) + +}) diff --git a/pkg/cobrautils/flag/path_array_win_test.go b/pkg/cobrautils/flag/path_array_win_test.go new file mode 100644 index 0000000000..3428e4d962 --- /dev/null +++ b/pkg/cobrautils/flag/path_array_win_test.go @@ -0,0 +1,34 @@ +//go:build windows +// +build windows + +package flag_test + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/spf13/pflag" + + . "github.com/open-component-model/ocm/pkg/cobrautils/flag" +) + +var _ = Describe("path flags", func() { + var flags *pflag.FlagSet + + BeforeEach(func() { + flags = pflag.NewFlagSet("test", pflag.ContinueOnError) + }) + + It("parse windows path", func() { + var val []string + PathArrayVarPF(flags, &val, "path", "p", nil, "help message") + flags.Parse([]string{"-p", `C:\foo\bar;E:\other\path`}) + Expect(val).To(Equal([]string{"C:/foo/bar", "E:/other/path"})) + }) + + It("parse default path", func() { + var val []string + PathArrayVarPF(flags, &val, "path", "p", []string{`C:\foo\bar`, `E:\other\path`}, "help message") + Expect(val).To(Equal([]string{"C:/foo/bar", "E:/other/path"})) + }) + +}) diff --git a/pkg/cobrautils/flag/path_test.go b/pkg/cobrautils/flag/path_test.go new file mode 100644 index 0000000000..034c5f83fe --- /dev/null +++ b/pkg/cobrautils/flag/path_test.go @@ -0,0 +1,34 @@ +//go:build !windows +// +build !windows + +package flag_test + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/spf13/pflag" + + . "github.com/open-component-model/ocm/pkg/cobrautils/flag" +) + +var _ = Describe("path flags", func() { + var flags *pflag.FlagSet + + BeforeEach(func() { + flags = pflag.NewFlagSet("test", pflag.ContinueOnError) + }) + + It("parse linux path", func() { + var val string + PathVarP(flags, &val, "path", "p", "", "help message") + flags.Parse([]string{"-p", `/t/bugrepo/postgresql-14.0.5.tgz`}) + Expect(val).To(Equal("/t/bugrepo/postgresql-14.0.5.tgz")) + }) + + It("parse default path", func() { + var val string + PathVarP(flags, &val, "path", "p", `/t/bugrepo/postgresql-14.0.5.tgz`, "help message") + Expect(val).To(Equal("/t/bugrepo/postgresql-14.0.5.tgz")) + }) + +}) diff --git a/pkg/cobrautils/flag/path_win_test.go b/pkg/cobrautils/flag/path_win_test.go new file mode 100644 index 0000000000..fb3efba3a8 --- /dev/null +++ b/pkg/cobrautils/flag/path_win_test.go @@ -0,0 +1,34 @@ +//go:build windows +// +build windows + +package flag_test + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/spf13/pflag" + + . "github.com/open-component-model/ocm/pkg/cobrautils/flag" +) + +var _ = Describe("path flags", func() { + var flags *pflag.FlagSet + + BeforeEach(func() { + flags = pflag.NewFlagSet("test", pflag.ContinueOnError) + }) + + It("parse windows path", func() { + var val string + PathVarPF(flags, &val, "path", "p", "", "help message") + flags.Parse([]string{"-p", `E:\t\bugrepo\postgresql-14.0.5.tgz`}) + Expect(val).To(Equal("E:/t/bugrepo/postgresql-14.0.5.tgz")) + }) + + It("parse default path", func() { + var val string + PathVarP(flags, &val, "path", "p", `E:\t\bugrepo\postgresql-14.0.5.tgz`, "help message") + Expect(val).To(Equal("E:/t/bugrepo/postgresql-14.0.5.tgz")) + }) + +}) diff --git a/pkg/cobrautils/flag/replace.go b/pkg/cobrautils/flag/replace.go index 5cc0f2bebe..d68c1e08b8 100644 --- a/pkg/cobrautils/flag/replace.go +++ b/pkg/cobrautils/flag/replace.go @@ -1,7 +1,3 @@ -// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Open Component Model contributors. -// -// SPDX-License-Identifier: Apache-2.0 - package flag import ( diff --git a/pkg/cobrautils/flagsets/types.go b/pkg/cobrautils/flagsets/types.go index bb1c165d77..02db17cbf0 100644 --- a/pkg/cobrautils/flagsets/types.go +++ b/pkg/cobrautils/flagsets/types.go @@ -1,7 +1,3 @@ -// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Open Component Model contributors. -// -// SPDX-License-Identifier: Apache-2.0 - package flagsets import ( @@ -149,6 +145,80 @@ func (o *StringArrayOption) Value() interface{} { return o.value } +// PathOptionType ////////////////////////////////////////////////////////////////////////////// + +type PathOptionType struct { + TypeOptionBase +} + +func NewPathOptionType(name string, description string) ConfigOptionType { + return &PathOptionType{ + TypeOptionBase: TypeOptionBase{name, description}, + } +} + +func (s *PathOptionType) Equal(optionType ConfigOptionType) bool { + return reflect.DeepEqual(s, optionType) +} + +func (s *PathOptionType) Create() Option { + return &PathOption{ + OptionBase: NewOptionBase(s), + } +} + +type PathOption struct { + OptionBase + value string +} + +var _ Option = (*PathOption)(nil) + +func (o *PathOption) AddFlags(fs *pflag.FlagSet) { + o.TweakFlag(flag.PathVarPF(fs, &o.value, o.otyp.GetName(), "", "", o.otyp.GetDescription())) +} + +func (o *PathOption) Value() interface{} { + return o.value +} + +// PathArrayOptionType ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +type PathArrayOptionType struct { + TypeOptionBase +} + +func NewPathArrayOptionType(name string, description string) ConfigOptionType { + return &PathArrayOptionType{ + TypeOptionBase: TypeOptionBase{name, description}, + } +} + +func (s *PathArrayOptionType) Equal(optionType ConfigOptionType) bool { + return reflect.DeepEqual(s, optionType) +} + +func (s *PathArrayOptionType) Create() Option { + return &PathArrayOption{ + OptionBase: NewOptionBase(s), + } +} + +type PathArrayOption struct { + OptionBase + value []string +} + +var _ Option = (*PathArrayOption)(nil) + +func (o *PathArrayOption) AddFlags(fs *pflag.FlagSet) { + o.TweakFlag(flag.PathArrayVarPF(fs, &o.value, o.otyp.GetName(), "", nil, o.otyp.GetDescription())) +} + +func (o *PathArrayOption) Value() interface{} { + return o.value +} + //////////////////////////////////////////////////////////////////////////////// type BoolOptionType struct {