func mergeHooks(terragruntOptions *options.TerragruntOptions, childHooks []Hook, parentHooks *[]Hook) {
	result := *parentHooks
	for _, child := range childHooks {
		parentHookWithSameName := getIndexOfHookWithName(result, child.Name)
		if parentHookWithSameName != -1 {
			terragruntOptions.Logger.Debugf("hook '%v' from child overriding parent", child.Name)
			result[parentHookWithSameName] = child
		} else {
			result = append(result, child)
		}
	}
	*parentHooks = result
}
Merge the hooks (before_hook and after_hook).

If a child's hook (before_hook or after_hook) has the same name a parent's hook,
then the child's hook will be selected (and the parent's ignored)
If a child's hook has a different name from all of the parent's hooks,
then the child's hook will be added to the end of the parent's.
Therefore, the child with the same name overrides the parent
package config
import (
	"encoding/json"
	"fmt"
	"path/filepath"
	"strings"
	"github.com/hashicorp/hcl/v2"
	"github.com/hashicorp/hcl/v2/hclwrite"
	"github.com/imdario/mergo"
	"github.com/zclconf/go-cty/cty"
	"github.com/gruntwork-io/terragrunt/errors"
	"github.com/gruntwork-io/terragrunt/options"
	"github.com/gruntwork-io/terragrunt/util"
)
const bareIncludeKey = ""
func parseIncludedConfig(
	includedConfig *IncludeConfig,
	terragruntOptions *options.TerragruntOptions,
	dependencyOutputs *cty.Value,
	decodeList []PartialDecodeSectionType,
) (*TerragruntConfig, error) {
	if includedConfig.Path == "" {
		return nil, errors.WithStackTrace(IncludedConfigMissingPath(terragruntOptions.TerragruntConfigPath))
	}
	includePath := includedConfig.Path
	if !filepath.IsAbs(includePath) {
		includePath = util.JoinPath(filepath.Dir(terragruntOptions.TerragruntConfigPath), includePath)
	}
	hasDependency, err := configFileHasDependencyBlock(includePath, terragruntOptions)
	if err != nil {
		return nil, err
	}
	if hasDependency && len(decodeList) > 0 {
		terragruntOptions.Logger.Warnf(
			"Included config %s can only be partially parsed during dependency graph formation for run-all command as it has a dependency block.",
			includePath,
		)
		return PartialParseConfigFile(includePath, terragruntOptions, includedConfig, decodeList)
	}
	return ParseConfigFile(includePath, terragruntOptions, includedConfig, dependencyOutputs)
}
func handleInclude(
	config *TerragruntConfig,
	trackInclude *TrackInclude,
	terragruntOptions *options.TerragruntOptions,
	dependencyOutputs *cty.Value,
) (*TerragruntConfig, error) {
	if trackInclude == nil {
		return nil, fmt.Errorf("You reached an impossible condition. This is most likely a bug in terragrunt. Please open an issue at github.com/gruntwork-io/terragrunt with this error message. Code: HANDLE_INCLUDE_NIL_INCLUDE_CONFIG")
	}
	includeList := trackInclude.CurrentList
	baseConfig := config
	for i := len(includeList) - 1; i >= 0; i-- {
		includeConfig := includeList[i]
		mergeStrategy, err := includeConfig.GetMergeStrategy()
		if err != nil {
			return config, err
		}
		parsedIncludeConfig, err := parseIncludedConfig(&includeConfig, terragruntOptions, dependencyOutputs, nil)
		if err != nil {
			return nil, err
		}
		switch mergeStrategy {
		case NoMerge:
			terragruntOptions.Logger.Debugf("Included config %s has strategy no merge: not merging config in.", includeConfig.Path)
		case ShallowMerge:
			terragruntOptions.Logger.Debugf("Included config %s has strategy shallow merge: merging config in (shallow).", includeConfig.Path)
			parsedIncludeConfig.Merge(baseConfig, terragruntOptions)
			baseConfig = parsedIncludeConfig
		case DeepMerge:
			terragruntOptions.Logger.Debugf("Included config %s has strategy deep merge: merging config in (deep).", includeConfig.Path)
			if err := parsedIncludeConfig.DeepMerge(baseConfig, terragruntOptions); err != nil {
				return nil, err
			}
			baseConfig = parsedIncludeConfig
		default:
			return nil, fmt.Errorf("You reached an impossible condition. This is most likely a bug in terragrunt. Please open an issue at github.com/gruntwork-io/terragrunt with this error message. Code: UNKNOWN_MERGE_STRATEGY_%s", mergeStrategy)
		}
	}
	return baseConfig, nil
}
func handleIncludePartial(
	config *TerragruntConfig,
	trackInclude *TrackInclude,
	terragruntOptions *options.TerragruntOptions,
	decodeList []PartialDecodeSectionType,
) (*TerragruntConfig, error) {
	if trackInclude == nil {
		return nil, fmt.Errorf("You reached an impossible condition. This is most likely a bug in terragrunt. Please open an issue at github.com/gruntwork-io/terragrunt with this error message. Code: HANDLE_INCLUDE_PARTIAL_NIL_INCLUDE_CONFIG")
	}
	includeList := trackInclude.CurrentList
	baseConfig := config
	for i := len(includeList) - 1; i >= 0; i-- {
		includeConfig := includeList[i]
		mergeStrategy, err := includeConfig.GetMergeStrategy()
		if err != nil {
			return nil, err
		}
		parsedIncludeConfig, err := partialParseIncludedConfig(&includeConfig, terragruntOptions, decodeList)
		if err != nil {
			return nil, err
		}
		switch mergeStrategy {
		case NoMerge:
			terragruntOptions.Logger.Debugf("[Partial] Included config %s has strategy no merge: not merging config in.", includeConfig.Path)
		case ShallowMerge:
			terragruntOptions.Logger.Debugf("[Partial] Included config %s has strategy shallow merge: merging config in (shallow).", includeConfig.Path)
			parsedIncludeConfig.Merge(baseConfig, terragruntOptions)
			baseConfig = parsedIncludeConfig
		case DeepMerge:
			terragruntOptions.Logger.Debugf("[Partial] Included config %s has strategy deep merge: merging config in (deep).", includeConfig.Path)
			if err := parsedIncludeConfig.DeepMerge(baseConfig, terragruntOptions); err != nil {
				return nil, err
			}
			baseConfig = parsedIncludeConfig
		default:
			return nil, fmt.Errorf("You reached an impossible condition. This is most likely a bug in terragrunt. Please open an issue at github.com/gruntwork-io/terragrunt with this error message. Code: UNKNOWN_MERGE_STRATEGY_%s_PARTIAL", mergeStrategy)
		}
	}
	return baseConfig, nil
}
func handleIncludeForDependency(
	childDecodedDependency terragruntDependency,
	trackInclude *TrackInclude,
	terragruntOptions *options.TerragruntOptions,
) (*terragruntDependency, error) {
	if trackInclude == nil {
		return nil, fmt.Errorf("You reached an impossible condition. This is most likely a bug in terragrunt. Please open an issue at github.com/gruntwork-io/terragrunt with this error message. Code: HANDLE_INCLUDE_DEPENDENCY_NIL_INCLUDE_CONFIG")
	}
	includeList := trackInclude.CurrentList
	baseDependencyBlock := childDecodedDependency.Dependencies
	for i := len(includeList) - 1; i >= 0; i-- {
		includeConfig := includeList[i]
		mergeStrategy, err := includeConfig.GetMergeStrategy()
		if err != nil {
			return nil, err
		}
		includedPartialParse, err := partialParseIncludedConfig(&includeConfig, terragruntOptions, []PartialDecodeSectionType{DependencyBlock})
		if err != nil {
			return nil, err
		}
		switch mergeStrategy {
		case NoMerge:
			terragruntOptions.Logger.Debugf("Included config %s has strategy no merge: not merging config in for dependency.", includeConfig.Path)
		case ShallowMerge:
			terragruntOptions.Logger.Debugf("Included config %s has strategy shallow merge: merging config in (shallow) for dependency.", includeConfig.Path)
			mergedDependencyBlock := mergeDependencyBlocks(includedPartialParse.TerragruntDependencies, baseDependencyBlock)
			baseDependencyBlock = mergedDependencyBlock
		case DeepMerge:
			terragruntOptions.Logger.Debugf("Included config %s has strategy deep merge: merging config in (deep) for dependency.", includeConfig.Path)
			mergedDependencyBlock, err := deepMergeDependencyBlocks(includedPartialParse.TerragruntDependencies, baseDependencyBlock)
			if err != nil {
				return nil, err
			}
			baseDependencyBlock = mergedDependencyBlock
		default:
			return nil, fmt.Errorf("You reached an impossible condition. This is most likely a bug in terragrunt. Please open an issue at github.com/gruntwork-io/terragrunt with this error message. Code: UNKNOWN_MERGE_STRATEGY_%s_DEPENDENCY", mergeStrategy)
		}
	}
	return &terragruntDependency{Dependencies: baseDependencyBlock}, nil
}
func (targetConfig *TerragruntConfig) Merge(sourceConfig *TerragruntConfig, terragruntOptions *options.TerragruntOptions) {
	if sourceConfig.DownloadDir != "" {
		targetConfig.DownloadDir = sourceConfig.DownloadDir
	}
	if sourceConfig.IamRole != "" {
		targetConfig.IamRole = sourceConfig.IamRole
	}
	if sourceConfig.IamAssumeRoleDuration != nil {
		targetConfig.IamAssumeRoleDuration = sourceConfig.IamAssumeRoleDuration
	}
	if sourceConfig.TerraformVersionConstraint != "" {
		targetConfig.TerraformVersionConstraint = sourceConfig.TerraformVersionConstraint
	}
	if sourceConfig.TerraformBinary != "" {
		targetConfig.TerraformBinary = sourceConfig.TerraformBinary
	}
	if sourceConfig.PreventDestroy != nil {
		targetConfig.PreventDestroy = sourceConfig.PreventDestroy
	}
	if sourceConfig.RetryMaxAttempts != nil {
		targetConfig.RetryMaxAttempts = sourceConfig.RetryMaxAttempts
	}
	if sourceConfig.RetrySleepIntervalSec != nil {
		targetConfig.RetrySleepIntervalSec = sourceConfig.RetrySleepIntervalSec
	}
	if sourceConfig.TerragruntVersionConstraint != "" {
		targetConfig.TerragruntVersionConstraint = sourceConfig.TerragruntVersionConstraint
	}
	targetConfig.Skip = sourceConfig.Skip
	if sourceConfig.RemoteState != nil {
		targetConfig.RemoteState = sourceConfig.RemoteState
	}
	if sourceConfig.Terraform != nil {
		if targetConfig.Terraform == nil {
			targetConfig.Terraform = sourceConfig.Terraform
		} else {
			if sourceConfig.Terraform.Source != nil {
				targetConfig.Terraform.Source = sourceConfig.Terraform.Source
			}
			mergeExtraArgs(terragruntOptions, sourceConfig.Terraform.ExtraArgs, &targetConfig.Terraform.ExtraArgs)
			mergeHooks(terragruntOptions, sourceConfig.Terraform.BeforeHooks, &targetConfig.Terraform.BeforeHooks)
			mergeHooks(terragruntOptions, sourceConfig.Terraform.AfterHooks, &targetConfig.Terraform.AfterHooks)
		}
	}
	if sourceConfig.Dependencies != nil {
		if targetConfig.Dependencies == nil {
			targetConfig.Dependencies = sourceConfig.Dependencies
		} else {
			targetConfig.Dependencies.Merge(sourceConfig.Dependencies)
		}
	}
	if sourceConfig.RetryableErrors != nil {
		targetConfig.RetryableErrors = sourceConfig.RetryableErrors
	}
	for key, val := range sourceConfig.GenerateConfigs {
		targetConfig.GenerateConfigs[key] = val
	}
	if sourceConfig.Inputs != nil {
		targetConfig.Inputs = mergeInputs(sourceConfig.Inputs, targetConfig.Inputs)
	}
}
func (targetConfig *TerragruntConfig) DeepMerge(sourceConfig *TerragruntConfig, terragruntOptions *options.TerragruntOptions) error {
	if sourceConfig.DownloadDir != "" {
		targetConfig.DownloadDir = sourceConfig.DownloadDir
	}
	if sourceConfig.IamRole != "" {
		targetConfig.IamRole = sourceConfig.IamRole
	}
	if sourceConfig.IamAssumeRoleDuration != nil {
		targetConfig.IamAssumeRoleDuration = sourceConfig.IamAssumeRoleDuration
	}
	if sourceConfig.TerraformVersionConstraint != "" {
		targetConfig.TerraformVersionConstraint = sourceConfig.TerraformVersionConstraint
	}
	if sourceConfig.TerraformBinary != "" {
		targetConfig.TerraformBinary = sourceConfig.TerraformBinary
	}
	if sourceConfig.PreventDestroy != nil {
		targetConfig.PreventDestroy = sourceConfig.PreventDestroy
	}
	if sourceConfig.RetryMaxAttempts != nil {
		targetConfig.RetryMaxAttempts = sourceConfig.RetryMaxAttempts
	}
	if sourceConfig.RetrySleepIntervalSec != nil {
		targetConfig.RetrySleepIntervalSec = sourceConfig.RetrySleepIntervalSec
	}
	if sourceConfig.TerragruntVersionConstraint != "" {
		targetConfig.TerragruntVersionConstraint = sourceConfig.TerragruntVersionConstraint
	}
	targetConfig.Skip = sourceConfig.Skip
	if sourceConfig.Dependencies != nil {
		resultModuleDependencies := &ModuleDependencies{}
		if targetConfig.Dependencies != nil {
			resultModuleDependencies.Paths = targetConfig.Dependencies.Paths
		}
		resultModuleDependencies.Paths = append(resultModuleDependencies.Paths, sourceConfig.Dependencies.Paths...)
		targetConfig.Dependencies = resultModuleDependencies
	}
	if sourceConfig.RetryableErrors != nil {
		targetConfig.RetryableErrors = append(targetConfig.RetryableErrors, sourceConfig.RetryableErrors...)
	}
	if sourceConfig.Terraform != nil {
		if targetConfig.Terraform == nil {
			targetConfig.Terraform = sourceConfig.Terraform
		} else {
			if sourceConfig.Terraform.Source != nil {
				targetConfig.Terraform.Source = sourceConfig.Terraform.Source
			}
			mergeExtraArgs(terragruntOptions, sourceConfig.Terraform.ExtraArgs, &targetConfig.Terraform.ExtraArgs)
			mergeHooks(terragruntOptions, sourceConfig.Terraform.BeforeHooks, &targetConfig.Terraform.BeforeHooks)
			mergeHooks(terragruntOptions, sourceConfig.Terraform.AfterHooks, &targetConfig.Terraform.AfterHooks)
		}
	}
	if sourceConfig.Inputs != nil {
		mergedInputs, err := deepMergeInputs(sourceConfig.Inputs, targetConfig.Inputs)
		if err != nil {
			return err
		}
		targetConfig.Inputs = mergedInputs
	}
	if sourceConfig.RemoteState != nil {
		targetConfig.RemoteState = sourceConfig.RemoteState
	}
	for key, val := range sourceConfig.GenerateConfigs {
		targetConfig.GenerateConfigs[key] = val
	}
	return nil
}
func mergeDependencyBlocks(targetDependencies []Dependency, sourceDependencies []Dependency) []Dependency {
	keys := []string{}
	dependencyBlocks := make(map[string]Dependency)
	for _, dep := range targetDependencies {
		dependencyBlocks[dep.Name] = dep
		keys = append(keys, dep.Name)
	}
	for _, dep := range sourceDependencies {
		_, hasSameKey := dependencyBlocks[dep.Name]
		if !hasSameKey {
			keys = append(keys, dep.Name)
		}
		dependencyBlocks[dep.Name] = dep
	}
	combinedDeps := []Dependency{}
	for _, key := range keys {
		combinedDeps = append(combinedDeps, dependencyBlocks[key])
	}
	return combinedDeps
}
func deepMergeDependencyBlocks(targetDependencies []Dependency, sourceDependencies []Dependency) ([]Dependency, error) {
	keys := []string{}
	dependencyBlocks := make(map[string]Dependency)
	for _, dep := range targetDependencies {
		dependencyBlocks[dep.Name] = dep
		keys = append(keys, dep.Name)
	}
	for _, dep := range sourceDependencies {
		sameKeyDep, hasSameKey := dependencyBlocks[dep.Name]
		if hasSameKey {
			sameKeyDepPtr := &sameKeyDep
			if err := sameKeyDepPtr.DeepMerge(dep); err != nil {
				return nil, err
			}
			dependencyBlocks[dep.Name] = *sameKeyDepPtr
		} else {
			dependencyBlocks[dep.Name] = dep
			keys = append(keys, dep.Name)
		}
	}
	combinedDeps := []Dependency{}
	for _, key := range keys {
		combinedDeps = append(combinedDeps, dependencyBlocks[key])
	}
	return combinedDeps, nil
}
func mergeExtraArgs(terragruntOptions *options.TerragruntOptions, childExtraArgs []TerraformExtraArguments, parentExtraArgs *[]TerraformExtraArguments) {
	result := *parentExtraArgs
	for _, child := range childExtraArgs {
		parentExtraArgsWithSameName := getIndexOfExtraArgsWithName(result, child.Name)
		if parentExtraArgsWithSameName != -1 {
			terragruntOptions.Logger.Debugf("extra_arguments '%v' from child overriding parent", child.Name)
			result[parentExtraArgsWithSameName] = child
		} else {
			result = append(result, child)
		}
	}
	*parentExtraArgs = result
}
func mergeInputs(childInputs map[string]interface{}, parentInputs map[string]interface{}) map[string]interface{} {
	out := map[string]interface{}{}
	for key, value := range parentInputs {
		out[key] = value
	}
	for key, value := range childInputs {
		out[key] = value
	}
	return out
}
func deepMergeInputs(childInputs map[string]interface{}, parentInputs map[string]interface{}) (map[string]interface{}, error) {
	out := map[string]interface{}{}
	for key, value := range parentInputs {
		out[key] = value
	}
	err := mergo.Merge(&out, childInputs, mergo.WithAppendSlice, mergo.WithOverride)
	return out, errors.WithStackTrace(err)
}