github.com/GoogleContainerTools/kpt@v1.0.0-beta.50.0.20240520170205-c25345ffcbee/internal/util/argutil/argutil.go (about) 1 // Copyright 2019 The kpt Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // The argutil package contains libraries for parsing commandline args. 16 package argutil 17 18 import ( 19 "context" 20 "fmt" 21 "os" 22 "path/filepath" 23 "strings" 24 25 "github.com/GoogleContainerTools/kpt/pkg/printer" 26 "sigs.k8s.io/kustomize/kyaml/errors" 27 ) 28 29 var ErrMultiVersion = fmt.Errorf("at most 1 version permitted") 30 31 // ParseDirVersion parses given string of the form dir@verion and returns dir 32 // and version. 33 func ParseDirVersion(dirVer string) (string, string, error) { 34 if dirVer == "" { 35 return "", "", nil 36 } 37 if !strings.Contains(dirVer, "@") { 38 return dirVer, "", nil 39 } 40 parts := strings.Split(dirVer, "@") 41 if len(parts) > 2 { 42 return "", "", ErrMultiVersion 43 } 44 return parts[0], parts[1], nil 45 } 46 47 // ParseDirVersionWithDefaults parses given string of the form dir@version and 48 // returns dir and version with following defaults. 49 // if dir is missing, return current working directory 50 // if version is missing, return "master" 51 func ParseDirVersionWithDefaults(dirVer string) (string, string, error) { 52 dir, version, err := ParseDirVersion(dirVer) 53 if err != nil { 54 return dir, version, err 55 } 56 if dir == "" { 57 dir = "./" 58 } 59 if version == "" { 60 version = "master" 61 } 62 return dir, version, nil 63 } 64 65 // ParseFieldPath parse a flag value into a field path 66 // TODO(pwittrock): Extract this into lib.kpt.dev 67 func ParseFieldPath(path string) ([]string, error) { 68 // fixup '\.' so we don't split on it 69 match := strings.ReplaceAll(path, "\\.", "$$$$") 70 parts := strings.Split(match, ".") 71 for i := range parts { 72 parts[i] = strings.ReplaceAll(parts[i], "$$$$", ".") 73 } 74 75 // split the list index from the list field 76 var newParts []string 77 for i := range parts { 78 if !strings.Contains(parts[i], "[") { 79 newParts = append(newParts, parts[i]) 80 continue 81 } 82 p := strings.Split(parts[i], "[") 83 if len(p) != 2 { 84 return nil, errors.Errorf("unrecognized path element: %s. "+ 85 "Should be of the form 'list[field=value]'", parts[i]) 86 } 87 p[1] = "[" + p[1] 88 newParts = append(newParts, p[0], p[1]) 89 } 90 return newParts, nil 91 } 92 93 // ResolveSymlink returns the resolved symlink path for the input path 94 func ResolveSymlink(ctx context.Context, path string) (string, error) { 95 isSymlink := false 96 f, err := os.Lstat(path) 97 if err == nil { 98 // this step only helps with printing WARN message by checking if the input 99 // path has symlink, so do not error out at this phase and let 100 // filepath.EvalSymlinks(path) handle the cases 101 if f.Mode().Type() == os.ModeSymlink { 102 isSymlink = true 103 } 104 } 105 rp, err := filepath.EvalSymlinks(path) 106 if err != nil { 107 return "", err 108 } 109 if isSymlink { 110 fmt.Fprintf(printer.FromContextOrDie(ctx).ErrStream(), "[WARN] resolved symlink %q to %q, please note that the symlinks within the package are ignored\n", path, rp) 111 } 112 return rp, nil 113 }