diff --git a/hooks/_common.sh b/hooks/_common.sh index ae221b4de..8011e45e1 100644 --- a/hooks/_common.sh +++ b/hooks/_common.sh @@ -55,6 +55,43 @@ function common::parse_cmdline { done } +####################################################################### +# This is a workaround to improve performance when all files are passed +# See: https://github.com/antonbabenko/pre-commit-terraform/issues/309 +# Arguments: +# hook_id (string) hook ID, see `- id` for details in .pre-commit-hooks.yaml file +# files (array) filenames to check +# Outputs: +# Return 0 if `-a|--all` arg was passed to `pre-commit` +####################################################################### +function common::is_hook_run_on_whole_repo { + local -r hook_id="$1" + shift 1 + local -a -r files=("$@") + # get directory containing `.pre-commit-hooks.yaml` file + local -r root_config_dir="$(dirname "$(dirname "$(realpath "${BASH_SOURCE[0]}")")")" + # get included and excluded files from .pre-commit-hooks.yaml file + local -r hook_config_block=$(sed -n "/^- id: $hook_id$/,/^$/p" "$root_config_dir/.pre-commit-hooks.yaml") + local -r included_files=$(awk '$1 == "files:" {print $2; exit}' <<< "$hook_config_block") + local -r excluded_files=$(awk '$1 == "exclude:" {print $2; exit}' <<< "$hook_config_block") + # sorted string with the files passed to the hook by pre-commit + local -r files_to_check=$(printf '%s\n' "${files[@]}" | sort | tr '\n' ' ') + # git ls-files sorted string + local all_files_that_can_be_checked + + if [ -z "$excluded_files" ]; then + all_files_that_can_be_checked=$(git ls-files | sort | grep -e "$included_files" | tr '\n' ' ') + else + all_files_that_can_be_checked=$(git ls-files | sort | grep -e "$included_files" | grep -v -e "$excluded_files" | tr '\n' ' ') + fi + + if [ "$files_to_check" == "$all_files_that_can_be_checked" ]; then + return 0 + else + return 1 + fi +} + ####################################################################### # Hook execution boilerplate logic which is common to hooks, that run # on per dir basis. @@ -64,13 +101,23 @@ function common::parse_cmdline { # 3. Complete hook execution and return exit code # Arguments: # args (string with array) arguments that configure wrapped tool behavior +# hook_id (string) hook ID, see `- id` for details in .pre-commit-hooks.yaml file # files (array) filenames to check ####################################################################### function common::per_dir_hook { local -r args="$1" - shift 1 + local -r hook_id="$2" + shift 2 local -a -r files=("$@") + # check is (optional) function defined + if [ "$(type -t run_hook_on_whole_repo)" == function ] && + # check is hook run via `pre-commit run --all` + common::is_hook_run_on_whole_repo "$hook_id" "${files[@]}"; then + run_hook_on_whole_repo "$args" + exit 0 + fi + # consume modified files passed from pre-commit so that # hook runs against only those relevant directories local index=0 diff --git a/hooks/infracost_breakdown.sh b/hooks/infracost_breakdown.sh index dc7bc51b1..86fa86499 100755 --- a/hooks/infracost_breakdown.sh +++ b/hooks/infracost_breakdown.sh @@ -1,6 +1,10 @@ #!/usr/bin/env bash set -eo pipefail +# globals variables +# hook ID, see `- id` for details in .pre-commit-hooks.yaml file +# shellcheck disable=SC2034 # Unused var. +readonly HOOK_ID='infracost_breakdown' # shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines readonly SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" # shellcheck source=_common.sh diff --git a/hooks/terraform_docs.sh b/hooks/terraform_docs.sh index e71aae474..9bfc94003 100755 --- a/hooks/terraform_docs.sh +++ b/hooks/terraform_docs.sh @@ -1,6 +1,10 @@ #!/usr/bin/env bash set -eo pipefail +# globals variables +# hook ID, see `- id` for details in .pre-commit-hooks.yaml file +# shellcheck disable=SC2034 # Unused var. +readonly HOOK_ID='terraform_docs' # shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines readonly SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" # shellcheck source=_common.sh diff --git a/hooks/terraform_fmt.sh b/hooks/terraform_fmt.sh index 84746a62b..62e70810e 100755 --- a/hooks/terraform_fmt.sh +++ b/hooks/terraform_fmt.sh @@ -1,6 +1,10 @@ #!/usr/bin/env bash set -eo pipefail +# globals variables +# hook ID, see `- id` for details in .pre-commit-hooks.yaml file +# shellcheck disable=SC2034 # Unused var. +readonly HOOK_ID='terraform_fmt' # shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines readonly SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" # shellcheck source=_common.sh diff --git a/hooks/terraform_providers_lock.sh b/hooks/terraform_providers_lock.sh index 510ff3b4d..011ee45eb 100755 --- a/hooks/terraform_providers_lock.sh +++ b/hooks/terraform_providers_lock.sh @@ -2,6 +2,9 @@ set -eo pipefail +# globals variables +# hook ID, see `- id` for details in .pre-commit-hooks.yaml file +readonly HOOK_ID='terraform_providers_lock' # shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines readonly SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" # shellcheck source=_common.sh @@ -11,7 +14,7 @@ function main { common::initialize "$SCRIPT_DIR" common::parse_cmdline "$@" # shellcheck disable=SC2153 # False positive - common::per_dir_hook "${ARGS[*]}" "${FILES[@]}" + common::per_dir_hook "${ARGS[*]}" "$HOOK_ID" "${FILES[@]}" } ####################################################################### diff --git a/hooks/terraform_tflint.sh b/hooks/terraform_tflint.sh index b3788a92e..f89ab58bf 100755 --- a/hooks/terraform_tflint.sh +++ b/hooks/terraform_tflint.sh @@ -2,6 +2,9 @@ set -eo pipefail +# globals variables +# hook ID, see `- id` for details in .pre-commit-hooks.yaml file +readonly HOOK_ID='terraform_tflint' # shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines readonly SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" # shellcheck source=_common.sh @@ -14,7 +17,7 @@ function main { # shellcheck disable=SC2178 # It's the simplest syntax for that case ARGS=${ARGS[*]/__GIT_WORKING_DIR__/$(pwd)\/} # shellcheck disable=SC2128 # It's the simplest syntax for that case - common::per_dir_hook "$ARGS" "${FILES[@]}" + common::per_dir_hook "$ARGS" "$HOOK_ID" "${FILES[@]}" } ####################################################################### diff --git a/hooks/terraform_tfsec.sh b/hooks/terraform_tfsec.sh index f39c0285d..b46dff426 100755 --- a/hooks/terraform_tfsec.sh +++ b/hooks/terraform_tfsec.sh @@ -1,6 +1,9 @@ #!/usr/bin/env bash set -eo pipefail +# globals variables +# hook ID, see `- id` for details in .pre-commit-hooks.yaml file +readonly HOOK_ID='terraform_tfsec' # shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines readonly SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" # shellcheck source=_common.sh @@ -13,7 +16,7 @@ function main { # shellcheck disable=SC2178 # It's the simplest syntax for that case ARGS=${ARGS[*]/__GIT_WORKING_DIR__/$(pwd)\/} # shellcheck disable=SC2128 # It's the simplest syntax for that case - common::per_dir_hook "$ARGS" "${FILES[@]}" + common::per_dir_hook "$ARGS" "$HOOK_ID" "${FILES[@]}" } ####################################################################### diff --git a/hooks/terraform_validate.sh b/hooks/terraform_validate.sh index a123d5fbd..13db4b060 100755 --- a/hooks/terraform_validate.sh +++ b/hooks/terraform_validate.sh @@ -1,6 +1,10 @@ #!/usr/bin/env bash set -eo pipefail +# globals variables +# hook ID, see `- id` for details in .pre-commit-hooks.yaml file +# shellcheck disable=SC2034 # Unused var. +readonly HOOK_ID='terraform_validate' # shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines readonly SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" # shellcheck source=_common.sh diff --git a/hooks/terragrunt_fmt.sh b/hooks/terragrunt_fmt.sh index c750241df..4822fc3f8 100755 --- a/hooks/terragrunt_fmt.sh +++ b/hooks/terragrunt_fmt.sh @@ -1,6 +1,9 @@ #!/usr/bin/env bash set -eo pipefail +# globals variables +# hook ID, see `- id` for details in .pre-commit-hooks.yaml file +readonly HOOK_ID='terragrunt_fmt' # shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines readonly SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" # shellcheck source=_common.sh @@ -10,7 +13,7 @@ function main { common::initialize "$SCRIPT_DIR" common::parse_cmdline "$@" # shellcheck disable=SC2153 # False positive - common::per_dir_hook "${ARGS[*]}" "${FILES[@]}" + common::per_dir_hook "${ARGS[*]}" "$HOOK_ID" "${FILES[@]}" } ####################################################################### diff --git a/hooks/terragrunt_validate.sh b/hooks/terragrunt_validate.sh index e68497eea..58e20c46e 100755 --- a/hooks/terragrunt_validate.sh +++ b/hooks/terragrunt_validate.sh @@ -1,6 +1,9 @@ #!/usr/bin/env bash set -eo pipefail +# globals variables +# hook ID, see `- id` for details in .pre-commit-hooks.yaml file +readonly HOOK_ID='terragrunt_validate' # shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines readonly SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" # shellcheck source=_common.sh @@ -10,7 +13,7 @@ function main { common::initialize "$SCRIPT_DIR" common::parse_cmdline "$@" # shellcheck disable=SC2153 # False positive - common::per_dir_hook "${ARGS[*]}" "${FILES[@]}" + common::per_dir_hook "${ARGS[*]}" "$HOOK_ID" "${FILES[@]}" } ####################################################################### diff --git a/hooks/terrascan.sh b/hooks/terrascan.sh index 31315aadd..5c6415456 100755 --- a/hooks/terrascan.sh +++ b/hooks/terrascan.sh @@ -1,6 +1,9 @@ #!/usr/bin/env bash set -eo pipefail +# globals variables +# hook ID, see `- id` for details in .pre-commit-hooks.yaml file +readonly HOOK_ID='terrascan' # shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines readonly SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" # shellcheck source=_common.sh @@ -10,7 +13,7 @@ function main { common::initialize "$SCRIPT_DIR" common::parse_cmdline "$@" # shellcheck disable=SC2153 # False positive - common::per_dir_hook "${ARGS[*]}" "${FILES[@]}" + common::per_dir_hook "${ARGS[*]}" "$HOOK_ID" "${FILES[@]}" } ####################################################################### @@ -37,4 +40,22 @@ function per_dir_hook_unique_part { return $exit_code } +####################################################################### +# Unique part of `common::per_dir_hook`. The function is executed one time +# in the root git repo +# Arguments: +# args (string with array) arguments that configure wrapped tool behavior +####################################################################### +function run_hook_on_whole_repo { + local -r args="$1" + + # pass the arguments to hook + # shellcheck disable=SC2068 # hook fails when quoting is used ("$arg[@]") + terrascan scan -i terraform ${args[@]} + + # return exit code to common::per_dir_hook + local exit_code=$? + return $exit_code +} + [ "${BASH_SOURCE[0]}" != "$0" ] || main "$@"