github.com/osievert/jfrog-cli-core@v1.2.7/artifactory/utils/argsutils.go (about)

     1  package utils
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/jfrog/jfrog-client-go/utils/errorutils"
    10  	"github.com/mattn/go-shellwords"
    11  )
    12  
    13  // Removes the provided flag and value from the command arguments
    14  func RemoveFlagFromCommand(args *[]string, flagIndex, flagValueIndex int) {
    15  	argsCopy := *args
    16  	// Remove flag from Command if required.
    17  	if flagIndex != -1 {
    18  		argsCopy = append(argsCopy[:flagIndex], argsCopy[flagValueIndex+1:]...)
    19  	}
    20  	*args = argsCopy
    21  }
    22  
    23  // Find value of required CLI flag in Command.
    24  // If flag does not exist, the returned index is -1 and nil is returned as the error.
    25  // Return values:
    26  // err - error if flag exists but failed to extract its value.
    27  // flagIndex - index of flagName in Command.
    28  // flagValueIndex - index in Command in which the value of the flag exists.
    29  // flagValue - value of flagName.
    30  func FindFlag(flagName string, args []string) (flagIndex, flagValueIndex int, flagValue string, err error) {
    31  	flagIndex = -1
    32  	flagValueIndex = -1
    33  	for index, arg := range args {
    34  		// Check current argument.
    35  		if !strings.HasPrefix(arg, flagName) {
    36  			continue
    37  		}
    38  
    39  		// Get flag value.
    40  		flagValue, flagValueIndex, err = getFlagValueAndValueIndex(flagName, args, index)
    41  		if err != nil {
    42  			return
    43  		}
    44  
    45  		// If was not the correct flag, continue looking.
    46  		if flagValueIndex == -1 {
    47  			continue
    48  		}
    49  
    50  		// Return value.
    51  		flagIndex = index
    52  		return
    53  	}
    54  
    55  	// Flag not found.
    56  	return
    57  }
    58  
    59  // Boolean flag can be provided in one of the following forms:
    60  // 1. --flag=value, where value can be true/false
    61  // 2. --flag, here the value is true
    62  // Return values:
    63  // flagIndex - index of flagName in args.
    64  // flagValue - value of flagName.
    65  // err - error if flag exists, but we failed to extract its value.
    66  // If flag does not exist flagIndex = -1 with false value and nil error.
    67  func FindBooleanFlag(flagName string, args []string) (flagIndex int, flagValue bool, err error) {
    68  	var arg string
    69  	for flagIndex, arg = range args {
    70  		if strings.HasPrefix(arg, flagName) {
    71  			value := strings.TrimPrefix(arg, flagName)
    72  			if len(value) == 0 {
    73  				flagValue = true
    74  			} else if strings.HasPrefix(value, "=") {
    75  				flagValue, err = strconv.ParseBool(value[1:])
    76  			} else {
    77  				continue
    78  			}
    79  			return
    80  		}
    81  	}
    82  	return -1, false, nil
    83  }
    84  
    85  // Get the provided flag's value, and the index of the value.
    86  // Value-index can either be same as flag's index, or the next one.
    87  // Return error if flag is found, but couldn't extract value.
    88  // If the provided index doesn't contain the searched flag, return flagIndex = -1.
    89  func getFlagValueAndValueIndex(flagName string, args []string, flagIndex int) (flagValue string, flagValueIndex int, err error) {
    90  	indexValue := args[flagIndex]
    91  
    92  	// Check if flag is in form '--key=value'
    93  	indexValue = strings.TrimPrefix(indexValue, flagName)
    94  	if strings.HasPrefix(indexValue, "=") {
    95  		if len(indexValue) > 1 {
    96  			return indexValue[1:], flagIndex, nil
    97  		}
    98  		return "", -1, errorutils.CheckError(errors.New(fmt.Sprintf("Flag %s is provided with empty value.", flagName)))
    99  	}
   100  
   101  	// Check if it is a different flag with same prefix, e.g --server-id-another
   102  	if len(indexValue) > 0 {
   103  		return "", -1, nil
   104  	}
   105  
   106  	// If reached here, expect the flag value in next argument.
   107  	if len(args) < flagIndex+2 {
   108  		// Flag value does not exist.
   109  		return "", -1, errorutils.CheckError(errors.New(fmt.Sprintf("Failed extracting value of provided flag: %s.", flagName)))
   110  	}
   111  
   112  	nextIndexValue := args[flagIndex+1]
   113  	// Don't allow next value to be a flag.
   114  	if strings.HasPrefix(nextIndexValue, "-") {
   115  		// Flag value does not exist.
   116  		return "", -1, errorutils.CheckError(errors.New(fmt.Sprintf("Failed extracting value of provided flag: %s.", flagName)))
   117  	}
   118  
   119  	return nextIndexValue, flagIndex + 1, nil
   120  }
   121  
   122  // Find the first match of any of the provided flags in args.
   123  // Return same values as FindFlag.
   124  func FindFlagFirstMatch(flags, args []string) (flagIndex, flagValueIndex int, flagValue string, err error) {
   125  	// Look for provided flags.
   126  	for _, flag := range flags {
   127  		flagIndex, flagValueIndex, flagValue, err = FindFlag(flag, args)
   128  		if err != nil {
   129  			return
   130  		}
   131  		if flagIndex != -1 {
   132  			// Found value for flag.
   133  			return
   134  		}
   135  	}
   136  	return
   137  }
   138  
   139  func ExtractBuildDetailsFromArgs(args []string) (cleanArgs []string, buildConfig *BuildConfiguration, err error) {
   140  	var flagIndex, valueIndex int
   141  	buildConfig = &BuildConfiguration{}
   142  	cleanArgs = append([]string(nil), args...)
   143  
   144  	// Extract build-info information from the args.
   145  	flagIndex, valueIndex, buildConfig.BuildName, err = FindFlag("--build-name", cleanArgs)
   146  	if err != nil {
   147  		return
   148  	}
   149  	RemoveFlagFromCommand(&cleanArgs, flagIndex, valueIndex)
   150  
   151  	flagIndex, valueIndex, buildConfig.BuildNumber, err = FindFlag("--build-number", cleanArgs)
   152  	if err != nil {
   153  		return
   154  	}
   155  	RemoveFlagFromCommand(&cleanArgs, flagIndex, valueIndex)
   156  
   157  	// Retrieve build name and build number from env if both missing
   158  	buildConfig.BuildName, buildConfig.BuildNumber = GetBuildNameAndNumber(buildConfig.BuildName, buildConfig.BuildNumber)
   159  	flagIndex, valueIndex, buildConfig.Module, err = FindFlag("--module", cleanArgs)
   160  	if err != nil {
   161  		return
   162  	}
   163  	RemoveFlagFromCommand(&cleanArgs, flagIndex, valueIndex)
   164  	err = ValidateBuildAndModuleParams(buildConfig)
   165  	return
   166  }
   167  
   168  func ExtractInsecureTlsFromArgs(args []string) (cleanArgs []string, insecureTls bool, err error) {
   169  	cleanArgs = append([]string(nil), args...)
   170  
   171  	flagIndex, insecureTls, err := FindBooleanFlag("--insecure-tls", args)
   172  	if err != nil {
   173  		return
   174  	}
   175  	RemoveFlagFromCommand(&cleanArgs, flagIndex, flagIndex)
   176  	return
   177  }
   178  
   179  // Iterate over each argument, if env variable is found (e.g $HOME) replace it with env value.
   180  func ParseArgs(args []string) ([]string, error) {
   181  	// Escape backslash & space
   182  	for i := 0; i < len(args); i++ {
   183  		args[i] = strings.ReplaceAll(args[i], `\`, `\\`)
   184  		if strings.Index(args[i], ` `) != -1 && !isQuote(args[i]) {
   185  			args[i] = strings.ReplaceAll(args[i], `"`, ``)
   186  			args[i] = strings.ReplaceAll(args[i], `'`, ``)
   187  			args[i] = `"` + args[i] + `"`
   188  		}
   189  	}
   190  	parser := shellwords.NewParser()
   191  	parser.ParseEnv = true
   192  	return parser.Parse(strings.Join(args, " "))
   193  }
   194  
   195  func isQuote(s string) bool {
   196  	return len(s) > 0 && ((s[0] == '"' && s[len(s)-1] == '"') || (s[0] == '\'' && s[len(s)-1] == '\''))
   197  }