Skip to content

Commit

Permalink
parse and validate some input parameters as filesystem-paths (#669)
Browse files Browse the repository at this point in the history
## Description

<!-- 
Please do not leave this blank 
This PR [adds/removes/fixes/replaces] the [feature/bug/etc]. 
-->

Please include a summary of the changes and the related issue. Please
also include relevant motivation and context. List any dependencies that
are required for this change.


## What type of PR is this? (check all applicable)

- [ ] 🍕 Feature
- [x] 🐛 Bug Fix
- [ ] 📝 Documentation Update
- [ ] 🎨 Style
- [ ] 🧑‍💻 Code Refactor
- [ ] 🔥 Performance Improvements
- [ ] ✅ Test
- [ ] 🤖 Build
- [ ] 🔁 CI
- [ ] 📦 Chore (Release)
- [ ] ⏩ Revert

## Related Tickets & Documents

- Fixes #648

## Added tests?

- [ ] 👍 yes
- [ ] 🙅 no, because they aren't needed
- [ ] 🙋 no, because I need help
- [ ] Separate ticket for tests # (issue/pr)

Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration


## Added to documentation?

- [ ] 📜 README.md
- [ ] 🙅 no documentation needed

## Checklist:

- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules
  • Loading branch information
hilmarf authored Feb 23, 2024
1 parent 929d0ea commit cb203c0
Show file tree
Hide file tree
Showing 12 changed files with 425 additions and 51 deletions.
58 changes: 29 additions & 29 deletions cmds/ocm/commands/ocmcmds/common/inputs/options/standard.go
Original file line number Diff line number Diff line change
@@ -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 (
Expand All @@ -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 !<base64>")

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 !<base64>")
)

// 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")
)
12 changes: 4 additions & 8 deletions cmds/ocm/commands/ocmcmds/common/inputs/utils.go
Original file line number Diff line number Diff line change
@@ -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"
)
Expand Down Expand Up @@ -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
}
Expand All @@ -50,14 +46,14 @@ 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)
if err != nil {
return "", err
}

return filepath.Join(wd, path), nil
return vfs.Join(fs, wd, path), nil
}
}
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down
64 changes: 64 additions & 0 deletions pkg/cobrautils/flag/path.go
Original file line number Diff line number Diff line change
@@ -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)
}
112 changes: 112 additions & 0 deletions pkg/cobrautils/flag/path_array.go
Original file line number Diff line number Diff line change
@@ -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)
}
34 changes: 34 additions & 0 deletions pkg/cobrautils/flag/path_array_test.go
Original file line number Diff line number Diff line change
@@ -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"}))
})

})
Loading

0 comments on commit cb203c0

Please sign in to comment.