func parseMultiStringKeyValueEnvVar(envVarName string) (map[string]string, error) {
	rawEnvVarVal := os.Getenv(envVarName)
	if rawEnvVarVal == "" {
		return map[string]string{}, nil
	}
	mappingsAsList := strings.Split(rawEnvVarVal, ",")
	return util.KeyValuePairStringListToMap(mappingsAsList)
}
Parses an environment variable that is encoded as a comma separated kv pair (e.g.,
`key1=value1,key2=value2,key3=value3`) and converts it to a map. Returns empty map if the environnment variable is
not set, and error if the environment variable is not encoded as a comma separated kv pair.
package cli
import (
	"encoding/json"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"github.com/gruntwork-io/terragrunt/config"
	"github.com/gruntwork-io/terragrunt/errors"
	"github.com/gruntwork-io/terragrunt/options"
	"github.com/gruntwork-io/terragrunt/util"
	"github.com/hashicorp/go-version"
	"github.com/sirupsen/logrus"
	"github.com/urfave/cli"
)
func ParseTerragruntOptions(cliContext *cli.Context) (*options.TerragruntOptions, error) {
	terragruntOptions, err := parseTerragruntOptionsFromArgs(cliContext.App.Version, cliContext.Args(), cliContext.App.Writer, cliContext.App.ErrWriter)
	if err != nil {
		return nil, err
	}
	return terragruntOptions, nil
}
func parseTerragruntOptionsFromArgs(terragruntVersion string, args []string, writer, errWriter io.Writer) (*options.TerragruntOptions, error) {
	defaultWorkingDir := os.Getenv("TERRAGRUNT_WORKING_DIR")
	if defaultWorkingDir == "" {
		currentDir, err := os.Getwd()
		if err != nil {
			return nil, errors.WithStackTrace(err)
		}
		defaultWorkingDir = currentDir
	}
	workingDir, err := parseStringArg(args, optWorkingDir, defaultWorkingDir)
	if err != nil {
		return nil, err
	}
	downloadDirRaw, err := parseStringArg(args, optDownloadDir, os.Getenv("TERRAGRUNT_DOWNLOAD"))
	if err != nil {
		return nil, err
	}
	if downloadDirRaw == "" {
		downloadDirRaw = util.JoinPath(workingDir, options.TerragruntCacheDir)
	}
	downloadDir, err := filepath.Abs(downloadDirRaw)
	if err != nil {
		return nil, errors.WithStackTrace(err)
	}
	terragruntConfigPath, err := parseStringArg(args, optTerragruntConfig, os.Getenv("TERRAGRUNT_CONFIG"))
	if err != nil {
		return nil, err
	}
	if terragruntConfigPath == "" {
		terragruntConfigPath = config.GetDefaultConfigPath(workingDir)
	}
	terragruntHclFilePath, err := parseStringArg(args, optTerragruntHCLFmt, "")
	if err != nil {
		return nil, err
	}
	awsProviderPatchOverrides, err := parseMutliStringKeyValueArg(args, optTerragruntOverrideAttr, nil)
	if err != nil {
		return nil, err
	}
	terraformPath, err := parseStringArg(args, optTerragruntTFPath, os.Getenv("TERRAGRUNT_TFPATH"))
	if err != nil {
		return nil, err
	}
	if terraformPath == "" {
		terraformPath = options.TERRAFORM_DEFAULT_PATH
	}
	terraformSource, err := parseStringArg(args, optTerragruntSource, os.Getenv("TERRAGRUNT_SOURCE"))
	if err != nil {
		return nil, err
	}
	terraformSourceMapEnvVar, err := parseMultiStringKeyValueEnvVar("TERRAGRUNT_SOURCE_MAP")
	if err != nil {
		return nil, err
	}
	terraformSourceMap, err := parseMutliStringKeyValueArg(args, optTerragruntSourceMap, terraformSourceMapEnvVar)
	if err != nil {
		return nil, err
	}
	sourceUpdate := parseBooleanArg(args, optTerragruntSourceUpdate, os.Getenv("TERRAGRUNT_SOURCE_UPDATE") == "true" || os.Getenv("TERRAGRUNT_SOURCE_UPDATE") == "1")
	ignoreDependencyErrors := parseBooleanArg(args, optTerragruntIgnoreDependencyErrors, false)
	ignoreDependencyOrder := parseBooleanArg(args, optTerragruntIgnoreDependencyOrder, false)
	ignoreExternalDependencies := parseBooleanArg(args, optTerragruntIgnoreExternalDependencies, false)
	includeExternalDependencies := parseBooleanArg(args, optTerragruntIncludeExternalDependencies, os.Getenv("TERRAGRUNT_INCLUDE_EXTERNAL_DEPENDENCIES") == "true")
	excludeDirs, err := parseMultiStringArg(args, optTerragruntExcludeDir, []string{})
	if err != nil {
		return nil, err
	}
	includeDirs, err := parseMultiStringArg(args, optTerragruntIncludeDir, []string{})
	if err != nil {
		return nil, err
	}
	strictInclude := parseBooleanArg(args, optTerragruntStrictInclude, false)
	logLevel, err := parseStringArg(args, optTerragruntLogLevel, util.GetDefaultLogLevel().String())
	if err != nil {
		return nil, err
	}
	loggingLevel, err := logrus.ParseLevel(logLevel)
	if err != nil {
		util.GlobalFallbackLogEntry.Errorf(err.Error())
		return nil, err
	}
	validateStrictMode := parseBooleanArg(args, optTerragruntStrictValidate, false)
	opts, err := options.NewTerragruntOptions(filepath.ToSlash(terragruntConfigPath))
	if err != nil {
		return nil, err
	}
	opts.OriginalTerragruntConfigPath = opts.TerragruntConfigPath
	debug := parseBooleanArg(args, optTerragruntDebug, os.Getenv("TERRAGRUNT_DEBUG") == "true" || os.Getenv("TERRAGRUNT_DEBUG") == "1")
	if debug {
		opts.Debug = true
	}
	envValue, envProvided := os.LookupEnv("TERRAGRUNT_PARALLELISM")
	parallelism, err := parseIntArg(args, optTerragruntParallelism, envValue, envProvided, options.DEFAULT_PARALLELISM)
	if err != nil {
		return nil, err
	}
	iamRoleOpts, err := parseIAMRoleOptions(args)
	if err != nil {
		return nil, err
	}
	opts.OriginalIAMRoleOptions = *iamRoleOpts
	opts.IAMRoleOptions = *iamRoleOpts
	opts.TerraformPath = filepath.ToSlash(terraformPath)
	opts.AutoInit = !parseBooleanArg(args, optTerragruntNoAutoInit, os.Getenv("TERRAGRUNT_AUTO_INIT") == "false")
	opts.AutoRetry = !parseBooleanArg(args, optTerragruntNoAutoRetry, os.Getenv("TERRAGRUNT_AUTO_RETRY") == "false")
	opts.NonInteractive = parseBooleanArg(args, optNonInteractive, os.Getenv("TF_INPUT") == "false" || os.Getenv("TF_INPUT") == "0")
	opts.TerraformCliArgs = filterTerragruntArgs(args)
	opts.OriginalTerraformCommand = util.FirstArg(opts.TerraformCliArgs)
	opts.TerraformCommand = util.FirstArg(opts.TerraformCliArgs)
	opts.WorkingDir = filepath.ToSlash(workingDir)
	opts.DownloadDir = filepath.ToSlash(downloadDir)
	opts.LogLevel = loggingLevel
	opts.ValidateStrict = validateStrictMode
	opts.Logger = util.CreateLogEntry("", loggingLevel)
	opts.Logger.Logger.SetOutput(errWriter)
	opts.RunTerragrunt = RunTerragrunt
	opts.Source = terraformSource
	opts.SourceMap = terraformSourceMap
	opts.SourceUpdate = sourceUpdate
	opts.TerragruntVersion, err = version.NewVersion(terragruntVersion)
	if err != nil {
		opts.TerragruntVersion, err = version.NewVersion("0.0")
		if err != nil {
			return nil, err
		}
	}
	opts.IgnoreDependencyErrors = ignoreDependencyErrors
	opts.IgnoreDependencyOrder = ignoreDependencyOrder
	opts.IgnoreExternalDependencies = ignoreExternalDependencies
	opts.IncludeExternalDependencies = includeExternalDependencies
	opts.Writer = writer
	opts.ErrWriter = errWriter
	opts.Env = parseEnvironmentVariables(os.Environ())
	opts.ExcludeDirs = excludeDirs
	opts.IncludeDirs = includeDirs
	opts.StrictInclude = strictInclude
	opts.Parallelism = parallelism
	opts.Check = parseBooleanArg(args, optTerragruntCheck, os.Getenv("TERRAGRUNT_CHECK") == "true")
	opts.HclFile = filepath.ToSlash(terragruntHclFilePath)
	opts.AwsProviderPatchOverrides = awsProviderPatchOverrides
	opts.JSONOut, err = parseStringArg(args, optTerragruntJSONOut, "terragrunt_rendered.json")
	if err != nil {
		return nil, err
	}
	return opts, nil
}
func filterTerraformExtraArgs(terragruntOptions *options.TerragruntOptions, terragruntConfig *config.TerragruntConfig) []string {
	out := []string{}
	cmd := util.FirstArg(terragruntOptions.TerraformCliArgs)
	for _, arg := range terragruntConfig.Terraform.ExtraArgs {
		for _, arg_cmd := range arg.Commands {
			if cmd == arg_cmd {
				lastArg := util.LastArg(terragruntOptions.TerraformCliArgs)
				skipVars := (cmd == "apply" || cmd == "destroy") && util.IsFile(lastArg)
				if arg.Arguments != nil {
					if skipVars {
						for _, a := range *arg.Arguments {
							if !strings.HasPrefix(a, "-var") {
								out = append(out, a)
							}
						}
					} else {
						out = append(out, *arg.Arguments...)
					}
				}
				if !skipVars {
					varFiles := arg.GetVarFiles(terragruntOptions.Logger)
					for _, file := range varFiles {
						out = append(out, fmt.Sprintf("-var-file=%s", file))
					}
				}
			}
		}
	}
	return out
}
func filterTerraformEnvVarsFromExtraArgs(terragruntOptions *options.TerragruntOptions, terragruntConfig *config.TerragruntConfig) map[string]string {
	out := map[string]string{}
	cmd := util.FirstArg(terragruntOptions.TerraformCliArgs)
	for _, arg := range terragruntConfig.Terraform.ExtraArgs {
		if arg.EnvVars == nil {
			continue
		}
		for _, argcmd := range arg.Commands {
			if cmd == argcmd {
				for k, v := range *arg.EnvVars {
					out[k] = v
				}
			}
		}
	}
	return out
}
func parseEnvironmentVariables(environment []string) map[string]string {
	environmentMap := make(map[string]string)
	for i := 0; i < len(environment); i++ {
		variableSplit := strings.SplitN(environment[i], "=", 2)
		if len(variableSplit) == 2 {
			environmentMap[strings.TrimSpace(variableSplit[0])] = variableSplit[1]
		}
	}
	return environmentMap
}
func filterTerragruntArgs(args []string) []string {
	out := []string{}
	for i := 0; i < len(args); i++ {
		arg := args[i]
		argWithoutPrefix := strings.TrimPrefix(arg, "--")
		if util.ListContainsElement(MULTI_MODULE_COMMANDS, arg) {
			continue
		}
		if util.ListContainsElement(allTerragruntStringOpts, argWithoutPrefix) {
			i = i + 1
			continue
		}
		if util.ListContainsElement(allTerragruntBooleanOpts, argWithoutPrefix) {
			continue
		}
		out = append(out, arg)
	}
	return out
}
func isDeprecatedOption(optionName string) (string, bool) {
	newOption, deprecated := DEPRECATED_ARGUMENTS[optionName]
	if deprecated {
		return newOption, true
	}
	return optionName, false
}
func parseIAMRoleOptions(args []string) (*options.IAMRoleOptions, error) {
	iamRole, err := parseStringArg(args, optTerragruntIAMRole, os.Getenv("TERRAGRUNT_IAM_ROLE"))
	if err != nil {
		return nil, err
	}
	envValue, envProvided := os.LookupEnv("TERRAGRUNT_IAM_ASSUME_ROLE_DURATION")
	iamAssumeRoleDuration, err := parseIntArg(args, optTerragruntIAMAssumeRoleDuration, envValue, envProvided, 0)
	if err != nil {
		return nil, err
	}
	defaultIamAssumeRoleSessionName := os.Getenv("TERRAGRUNT_IAM_ASSUME_ROLE_SESSION_NAME")
	iamAssumeRoleSessionName, err := parseStringArg(args, optTerragruntIAMAssumeRoleSessionName, defaultIamAssumeRoleSessionName)
	if err != nil {
		return nil, err
	}
	optsOut := &options.IAMRoleOptions{
		RoleARN:               iamRole,
		AssumeRoleDuration:    int64(iamAssumeRoleDuration),
		AssumeRoleSessionName: iamAssumeRoleSessionName,
	}
	return optsOut, nil
}
func parseBooleanArg(args []string, argName string, defaultValue bool) bool {
	for _, arg := range args {
		if arg == fmt.Sprintf("--%s", argName) {
			newOption, deprecated := isDeprecatedOption(argName)
			if deprecated {
				util.GlobalFallbackLogEntry.Warnf("Command line option %s is deprecated, please consider using %s", argName, newOption)
			}
			return true
		}
	}
	return defaultValue
}
func parseStringArg(args []string, argName string, defaultValue string) (string, error) {
	for i, arg := range args {
		if arg == fmt.Sprintf("--%s", argName) {
			newOption, deprecated := isDeprecatedOption(argName)
			if deprecated {
				util.GlobalFallbackLogEntry.Warnf("Command line option %s is deprecated, please consider using %s", argName, newOption)
			}
			if (i + 1) < len(args) {
				return args[i+1], nil
			} else {
				return "", errors.WithStackTrace(ArgMissingValue(argName))
			}
		}
	}
	return defaultValue, nil
}
func parseIntArg(args []string, argName string, envValue string, envProvided bool, defaultValue int) (int, error) {
	for i, arg := range args {
		if arg == fmt.Sprintf("--%s", argName) {
			newOption, deprecated := isDeprecatedOption(argName)
			if deprecated {
				util.GlobalFallbackLogEntry.Warnf("Command line option %s is deprecated, please consider using %s", argName, newOption)
			}
			if (i + 1) < len(args) {
				return strconv.Atoi(args[i+1])
			} else {
				return 0, errors.WithStackTrace(ArgMissingValue(argName))
			}
		}
	}
	if envProvided {
		return strconv.Atoi(envValue)
	} else {
		return defaultValue, nil
	}
}
func parseMultiStringArg(args []string, argName string, defaultValue []string) ([]string, error) {
	stringArgs := []string{}
	for i, arg := range args {
		if arg == fmt.Sprintf("--%s", argName) {
			newOption, deprecated := isDeprecatedOption(argName)
			if deprecated {
				util.GlobalFallbackLogEntry.Warnf("Command line option %s is deprecated, please consider using %s", argName, newOption)
			}
			if (i + 1) < len(args) {
				stringArgs = append(stringArgs, args[i+1])
			} else {
				return nil, errors.WithStackTrace(ArgMissingValue(argName))
			}
		}
	}
	if len(stringArgs) == 0 {
		return defaultValue, nil
	}
	return stringArgs, nil
}
func parseMutliStringKeyValueArg(args []string, argName string, defaultValue map[string]string) (map[string]string, error) {
	asList, err := parseMultiStringArg(args, argName, nil)
	if err != nil {
		return nil, err
	}
	if asList == nil {
		return defaultValue, nil
	}
	return util.KeyValuePairStringListToMap(asList)
}