From 1e9debc02f558156823fc1ddd5cf2b48b1593167 Mon Sep 17 00:00:00 2001 From: Luke <9834514+lexton@users.noreply.github.com> Date: Mon, 8 May 2023 11:32:06 -0400 Subject: [PATCH] feat: TFLint: Add `--hook-config=--delegate-chdir` to use `tflint -chdir` (#512) Co-authored-by: Maksym Vlasov --- README.md | 8 ++++++++ hooks/_common.sh | 31 ++++++++++++++++++++++++++++--- hooks/terraform_checkov.sh | 7 ++++++- hooks/terraform_fmt.sh | 7 ++++++- hooks/terraform_providers_lock.sh | 3 +++ hooks/terraform_tflint.sh | 17 +++++++++++++---- hooks/terraform_tfsec.sh | 7 ++++++- hooks/terraform_validate.sh | 9 +++++++-- hooks/terragrunt_fmt.sh | 7 ++++++- hooks/terragrunt_validate.sh | 7 ++++++- hooks/terrascan.sh | 7 ++++++- hooks/tfupdate.sh | 7 ++++++- 12 files changed, 101 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 2e57b1a..17d7d0f 100644 --- a/README.md +++ b/README.md @@ -604,6 +604,14 @@ To replicate functionality in `terraform_docs` hook: - --args=--config=__GIT_WORKING_DIR__/.tflint.hcl ``` +3. By default pre-commit-terraform performs directory switching into the terraform modules for you. If you want to delgate the directory changing to the binary - this will allow tflint to determine the full paths for error/warning messages, rather than just module relative paths. *Note: this requires `tflint>=0.44.0`.* For example: + + ```yaml + - id: terraform_tflint + args: + - --hook-config=--delegate-chdir + ``` + ### terraform_tfsec diff --git a/hooks/_common.sh b/hooks/_common.sh index 37b32d8..4f9c7f0 100644 --- a/hooks/_common.sh +++ b/hooks/_common.sh @@ -217,6 +217,25 @@ function common::per_dir_hook { ((index += 1)) done + # Lookup hook-config for modifiers that impact common behavior + local change_dir_in_unique_part=false + IFS=";" read -r -a configs <<< "${HOOK_CONFIG[*]}" + for c in "${configs[@]}"; do + IFS="=" read -r -a config <<< "$c" + key=${config[0]} + value=${config[1]} + + case $key in + --delegate-chdir) + # this flag will skip pushing and popping directories + # delegating the responsibility to the hooked plugin/binary + if [[ ! $value || $value == true ]]; then + change_dir_in_unique_part="delegate_chdir" + fi + ;; + esac + done + # preserve errexit status shopt -qo errexit && ERREXIT_IS_SET=true # allow hook to continue if exit_code is greater than 0 @@ -226,16 +245,22 @@ function common::per_dir_hook { # run hook for each path for dir_path in $(echo "${dir_paths[*]}" | tr ' ' '\n' | sort -u); do dir_path="${dir_path//__REPLACED__SPACE__/ }" - pushd "$dir_path" > /dev/null || continue - per_dir_hook_unique_part "$dir_path" "${args[@]}" + if [[ $change_dir_in_unique_part == false ]]; then + pushd "$dir_path" > /dev/null || continue + fi + + per_dir_hook_unique_part "$dir_path" "$change_dir_in_unique_part" "${args[@]}" local exit_code=$? if [ $exit_code -ne 0 ]; then final_exit_code=$exit_code fi - popd > /dev/null + if [[ $change_dir_in_unique_part == false ]]; then + popd > /dev/null + fi + done # restore errexit if it was set before the "for" loop diff --git a/hooks/terraform_checkov.sh b/hooks/terraform_checkov.sh index fe0de00..03ad208 100755 --- a/hooks/terraform_checkov.sh +++ b/hooks/terraform_checkov.sh @@ -31,6 +31,9 @@ function main { # Arguments: # dir_path (string) PATH to dir relative to git repo root. # Can be used in error logging +# change_dir_in_unique_part (string/false) Modifier which creates +# possibilities to use non-common chdir strategies. +# Availability depends on hook. # args (array) arguments that configure wrapped tool behavior # Outputs: # If failed - print out hook checks status @@ -38,7 +41,9 @@ function main { function per_dir_hook_unique_part { # shellcheck disable=SC2034 # Unused var. local -r dir_path="$1" - shift + # shellcheck disable=SC2034 # Unused var. + local -r change_dir_in_unique_part="$2" + shift 2 local -a -r args=("$@") checkov -d . "${args[@]}" diff --git a/hooks/terraform_fmt.sh b/hooks/terraform_fmt.sh index 73e3a3f..bb0b327 100755 --- a/hooks/terraform_fmt.sh +++ b/hooks/terraform_fmt.sh @@ -28,6 +28,9 @@ function main { # Arguments: # dir_path (string) PATH to dir relative to git repo root. # Can be used in error logging +# change_dir_in_unique_part (string/false) Modifier which creates +# possibilities to use non-common chdir strategies. +# Availability depends on hook. # args (array) arguments that configure wrapped tool behavior # Outputs: # If failed - print out hook checks status @@ -35,7 +38,9 @@ function main { function per_dir_hook_unique_part { # shellcheck disable=SC2034 # Unused var. local -r dir_path="$1" - shift + # shellcheck disable=SC2034 # Unused var. + local -r change_dir_in_unique_part="$2" + shift 2 local -a -r args=("$@") # pass the arguments to hook diff --git a/hooks/terraform_providers_lock.sh b/hooks/terraform_providers_lock.sh index a75a7e5..dedbcfd 100755 --- a/hooks/terraform_providers_lock.sh +++ b/hooks/terraform_providers_lock.sh @@ -25,6 +25,9 @@ function main { # Arguments: # dir_path (string) PATH to dir relative to git repo root. # Can be used in error logging +# change_dir_in_unique_part (string/false) Modifier which creates +# possibilities to use non-common chdir strategies. +# Availability depends on hook. # args (array) arguments that configure wrapped tool behavior # Outputs: # If failed - print out hook checks status diff --git a/hooks/terraform_tflint.sh b/hooks/terraform_tflint.sh index 1a74ce3..f26201a 100755 --- a/hooks/terraform_tflint.sh +++ b/hooks/terraform_tflint.sh @@ -28,7 +28,7 @@ function main { } || { local exit_code=$? common::colorify "red" "Command 'tflint --init' failed:" - echo "${TFLINT_INIT}" + echo -e "${TFLINT_INIT}" return ${exit_code} } @@ -41,21 +41,30 @@ function main { # Arguments: # dir_path (string) PATH to dir relative to git repo root. # Can be used in error logging +# change_dir_in_unique_part (string/false) Modifier which creates +# possibilities to use non-common chdir strategies. +# Availability depends on hook. # args (array) arguments that configure wrapped tool behavior # Outputs: # If failed - print out hook checks status ####################################################################### function per_dir_hook_unique_part { local -r dir_path="$1" - shift + local -r change_dir_in_unique_part="$2" + shift 2 local -a -r args=("$@") - TFLINT_OUTPUT=$(tflint "${args[@]}" 2>&1) + if [ "$change_dir_in_unique_part" == "delegate_chdir" ]; then + local dir_args="--chdir=$dir_path" + fi + + # shellcheck disable=SC2086 # we need to remove the arg if its unset + TFLINT_OUTPUT=$(tflint ${dir_args:-} "${args[@]}" 2>&1) local exit_code=$? if [ $exit_code -ne 0 ]; then common::colorify "yellow" "TFLint in $dir_path/:" - echo "$TFLINT_OUTPUT" + echo -e "$TFLINT_OUTPUT" fi # return exit code to common::per_dir_hook diff --git a/hooks/terraform_tfsec.sh b/hooks/terraform_tfsec.sh index 5351167..24b7d43 100755 --- a/hooks/terraform_tfsec.sh +++ b/hooks/terraform_tfsec.sh @@ -31,6 +31,9 @@ function main { # Arguments: # dir_path (string) PATH to dir relative to git repo root. # Can be used in error logging +# change_dir_in_unique_part (string/false) Modifier which creates +# possibilities to use non-common chdir strategies. +# Availability depends on hook. # args (array) arguments that configure wrapped tool behavior # Outputs: # If failed - print out hook checks status @@ -38,7 +41,9 @@ function main { function per_dir_hook_unique_part { # shellcheck disable=SC2034 # Unused var. local -r dir_path="$1" - shift + # shellcheck disable=SC2034 # Unused var. + local -r change_dir_in_unique_part="$2" + shift 2 local -a -r args=("$@") # pass the arguments to hook diff --git a/hooks/terraform_validate.sh b/hooks/terraform_validate.sh index 3e46948..d0a9dfb 100755 --- a/hooks/terraform_validate.sh +++ b/hooks/terraform_validate.sh @@ -70,13 +70,18 @@ function match_validate_errors { # Arguments: # dir_path (string) PATH to dir relative to git repo root. # Can be used in error logging +# change_dir_in_unique_part (string/false) Modifier which creates +# possibilities to use non-common chdir strategies. +# Availability depends on hook. # args (array) arguments that configure wrapped tool behavior # Outputs: # If failed - print out hook checks status ####################################################################### function per_dir_hook_unique_part { local -r dir_path="$1" - shift + # shellcheck disable=SC2034 # Unused var. + local -r change_dir_in_unique_part="$2" + shift 2 local -a -r args=("$@") local exit_code @@ -95,7 +100,7 @@ function per_dir_hook_unique_part { case $key in --retry-once-with-cleanup) - if [ $retry_once_with_cleanup ]; then + if [ $retry_once_with_cleanup ]; then common::colorify "yellow" 'Invalid hook config. Make sure that you specify not more than one "--retry-once-with-cleanup" flag' exit 1 fi diff --git a/hooks/terragrunt_fmt.sh b/hooks/terragrunt_fmt.sh index 279e78d..7c78b92 100755 --- a/hooks/terragrunt_fmt.sh +++ b/hooks/terragrunt_fmt.sh @@ -24,6 +24,9 @@ function main { # Arguments: # dir_path (string) PATH to dir relative to git repo root. # Can be used in error logging +# change_dir_in_unique_part (string/false) Modifier which creates +# possibilities to use non-common chdir strategies. +# Availability depends on hook. # args (array) arguments that configure wrapped tool behavior # Outputs: # If failed - print out hook checks status @@ -31,7 +34,9 @@ function main { function per_dir_hook_unique_part { # shellcheck disable=SC2034 # Unused var. local -r dir_path="$1" - shift + # shellcheck disable=SC2034 # Unused var. + local -r change_dir_in_unique_part="$2" + shift 2 local -a -r args=("$@") # pass the arguments to hook diff --git a/hooks/terragrunt_validate.sh b/hooks/terragrunt_validate.sh index 7d9e287..15e203b 100755 --- a/hooks/terragrunt_validate.sh +++ b/hooks/terragrunt_validate.sh @@ -24,6 +24,9 @@ function main { # Arguments: # dir_path (string) PATH to dir relative to git repo root. # Can be used in error logging +# change_dir_in_unique_part (string/false) Modifier which creates +# possibilities to use non-common chdir strategies. +# Availability depends on hook. # args (array) arguments that configure wrapped tool behavior # Outputs: # If failed - print out hook checks status @@ -31,7 +34,9 @@ function main { function per_dir_hook_unique_part { # shellcheck disable=SC2034 # Unused var. local -r dir_path="$1" - shift + # shellcheck disable=SC2034 # Unused var. + local -r change_dir_in_unique_part="$2" + shift 2 local -a -r args=("$@") # pass the arguments to hook diff --git a/hooks/terrascan.sh b/hooks/terrascan.sh index ad81aab..ac040b9 100755 --- a/hooks/terrascan.sh +++ b/hooks/terrascan.sh @@ -24,6 +24,9 @@ function main { # Arguments: # dir_path (string) PATH to dir relative to git repo root. # Can be used in error logging +# change_dir_in_unique_part (string/false) Modifier which creates +# possibilities to use non-common chdir strategies. +# Availability depends on hook. # args (array) arguments that configure wrapped tool behavior # Outputs: # If failed - print out hook checks status @@ -31,7 +34,9 @@ function main { function per_dir_hook_unique_part { # shellcheck disable=SC2034 # Unused var. local -r dir_path="$1" - shift + # shellcheck disable=SC2034 # Unused var. + local -r change_dir_in_unique_part="$2" + shift 2 local -a -r args=("$@") # pass the arguments to hook diff --git a/hooks/tfupdate.sh b/hooks/tfupdate.sh index ebfa7ce..d9a4829 100755 --- a/hooks/tfupdate.sh +++ b/hooks/tfupdate.sh @@ -34,6 +34,9 @@ function main { # Arguments: # dir_path (string) PATH to dir relative to git repo root. # Can be used in error logging +# change_dir_in_unique_part (string/false) Modifier which creates +# possibilities to use non-common chdir strategies. +# Availability depends on hook. # args (array) arguments that configure wrapped tool behavior # Outputs: # If failed - print out hook checks status @@ -41,7 +44,9 @@ function main { function per_dir_hook_unique_part { # shellcheck disable=SC2034 # Unused var. local -r dir_path="$1" - shift + # shellcheck disable=SC2034 # Unused var. + local -r change_dir_in_unique_part="$2" + shift 2 local -a -r args=("$@") # pass the arguments to hook