mirror of
https://github.com/tofuutils/pre-commit-opentofu.git
synced 2026-05-29 13:16:55 +02:00
feat: add terragrunt_validate_inputs hook
Signed-off-by: mketteringham <mketteringham@williamhill.co.uk>
This commit is contained in:
parent
4adc698948
commit
0ca9eca382
4 changed files with 235 additions and 2 deletions
|
|
@ -90,6 +90,14 @@
|
|||
files: (\.hcl)$
|
||||
exclude: \.terraform\/.*$
|
||||
|
||||
- id: terragrunt_validate_inputs
|
||||
name: Terragrunt validate inputs
|
||||
description: Validates Terragrunt unused and undefined inputs.
|
||||
entry: hooks/terragrunt_validate_inputs.sh
|
||||
language: script
|
||||
files: (\.hcl)$
|
||||
exclude: \.terraform\/.*$
|
||||
|
||||
- id: tofu_tfsec
|
||||
name: OpenTofu validate with tfsec (deprecated, use "tofu_trivy")
|
||||
description:
|
||||
|
|
|
|||
29
README.md
29
README.md
|
|
@ -60,7 +60,7 @@ If you are using `pre-commit-opentofu` already or want to support its developmen
|
|||
</sup></sub></sup></sub></sup></sub></sup></sub></sup></sub></sup></sub></sup></sub></sup></sub></sup></sub><br><br>
|
||||
* [`checkov`](https://github.com/bridgecrewio/checkov) required for `tofu_checkov` hook.
|
||||
* [`terraform-docs`](https://github.com/terraform-docs/terraform-docs) required for `tofu_docs` hook.
|
||||
* [`terragrunt`](https://terragrunt.gruntwork.io/docs/getting-started/install/) required for `terragrunt_validate` hook.
|
||||
* [`terragrunt`](https://terragrunt.gruntwork.io/docs/getting-started/install/) required for `terragrunt_validate` and `terragrunt_validate_inputs` hooks.
|
||||
* [`terrascan`](https://github.com/tenable/terrascan) required for `terrascan` hook.
|
||||
* [`TFLint`](https://github.com/terraform-linters/tflint) required for `tofu_tflint` hook.
|
||||
* [`TFSec`](https://github.com/liamg/tfsec) required for `tofu_tfsec` hook.
|
||||
|
|
@ -266,6 +266,32 @@ TAG=latest
|
|||
docker run --rm --entrypoint cat tofuutils/pre-commit-opentofu:$TAG /usr/bin/tools_versions_info
|
||||
```
|
||||
|
||||
### Example: Terragrunt Input Validation
|
||||
|
||||
Use `terragrunt_validate_inputs` to check that Terragrunt inputs line up with the module variables they are passed into:
|
||||
|
||||
```yaml
|
||||
repos:
|
||||
- repo: https://github.com/tofuutils/pre-commit-opentofu
|
||||
rev: <VERSION> # Get the latest from: https://github.com/tofuutils/pre-commit-opentofu/releases
|
||||
hooks:
|
||||
- id: terragrunt_fmt
|
||||
- id: terragrunt_validate_inputs
|
||||
args:
|
||||
- --args=--terragrunt-strict-validate
|
||||
```
|
||||
|
||||
> **Note**: This hook automatically uses `terragrunt validate-inputs` for older Terragrunt releases and `terragrunt hcl validate --inputs` for newer releases.
|
||||
>
|
||||
> If Terragrunt reports intermittent `.terragrunt-cache` download or `file exists` errors in your repository, run this hook serially in your consumer configuration:
|
||||
>
|
||||
> ```yaml
|
||||
> - id: terragrunt_validate_inputs
|
||||
> require_serial: true
|
||||
> args:
|
||||
> - --args=--terragrunt-strict-validate
|
||||
> ```
|
||||
|
||||
## Available Hooks
|
||||
|
||||
There are several [pre-commit](https://pre-commit.com/) hooks to keep OpenTofu configurations (both `*.tf` and `*.tfvars`) and Terragrunt configurations (`*.hcl`) in a good shape:
|
||||
|
|
@ -286,6 +312,7 @@ There are several [pre-commit](https://pre-commit.com/) hooks to keep OpenTofu c
|
|||
| `tofu_validate` | Validates all Terraform configuration files. [Hook notes](#tofu_validate) | `jq`, only for `--retry-once-with-cleanup` flag |
|
||||
| `terragrunt_fmt` | Reformat all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) to a canonical format. | `terragrunt` |
|
||||
| `terragrunt_validate` | Validates all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) | `terragrunt` |
|
||||
| `terragrunt_validate_inputs` | Validates Terragrunt unused and undefined inputs. | `terragrunt` |
|
||||
| `tofu_wrapper_module_for_each` | Generates OpenTofu wrappers with `for_each` in module. [Hook notes](#terraform_wrapper_module_for_each) | `hcledit` |
|
||||
| `terrascan` | [terrascan](https://github.com/tenable/terrascan) Detect compliance and security violations. [Hook notes](#terrascan) | `terrascan` |
|
||||
| `tfupdate` | [tfupdate](https://github.com/minamijoyo/tfupdate) Update version constraints of OpenTofu core, providers, and modules. [Hook notes](#tfupdate) | `tfupdate` |
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ function common::parse_and_export_env_vars {
|
|||
while true; do
|
||||
# Check if at least 1 env var exists in `$arg`
|
||||
# shellcheck disable=SC2016 # '${' should not be expanded
|
||||
if [[ "$arg" =~ .*'${'[A-Z_][A-Z0-9_]+?'}'.* ]]; then
|
||||
if [[ "$arg" =~ .*'${'[A-Z_][A-Z0-9_]*'}'.* ]]; then
|
||||
# Get `ENV_VAR` from `.*${ENV_VAR}.*`
|
||||
local env_var_name=${arg#*$\{}
|
||||
env_var_name=${env_var_name%%\}*}
|
||||
|
|
|
|||
198
hooks/terragrunt_validate_inputs.sh
Executable file
198
hooks/terragrunt_validate_inputs.sh
Executable file
|
|
@ -0,0 +1,198 @@
|
|||
#!/usr/bin/env bash
|
||||
set -eo pipefail
|
||||
|
||||
# globals variables
|
||||
# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines
|
||||
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
|
||||
# shellcheck source=_common.sh
|
||||
. "$SCRIPT_DIR/_common.sh"
|
||||
|
||||
function main {
|
||||
common::initialize "$SCRIPT_DIR"
|
||||
common::parse_cmdline "$@"
|
||||
common::export_provided_env_vars "${ENV_VARS[@]}"
|
||||
common::parse_and_export_env_vars
|
||||
# JFYI: terragrunt validate color already suppressed via PRE_COMMIT_COLOR=never
|
||||
|
||||
if terragrunt_version_ge_0_78; then
|
||||
normalize_validate_args_for_modern_terragrunt
|
||||
readonly SUBCOMMAND=("hcl" "validate" "--inputs")
|
||||
readonly RUN_ALL_SUBCOMMAND=("run" "--all" "hcl" "validate" "--inputs")
|
||||
|
||||
# shellcheck disable=SC2153 # False positive
|
||||
common::per_dir_hook "$HOOK_ID" "${#ARGS[@]}" "${ARGS[@]}" "${FILES[@]}"
|
||||
return
|
||||
fi
|
||||
|
||||
run_legacy_validate_inputs
|
||||
}
|
||||
|
||||
function normalize_validate_args_for_modern_terragrunt {
|
||||
local arg_idx
|
||||
|
||||
for arg_idx in "${!ARGS[@]}"; do
|
||||
case "${ARGS[$arg_idx]}" in
|
||||
--terragrunt-strict-validate|--strict-validate)
|
||||
ARGS[$arg_idx]="--strict"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
function terragrunt_version_ge_0_78 {
|
||||
local version_raw
|
||||
local version
|
||||
local major
|
||||
local minor
|
||||
|
||||
version_raw=$(terragrunt --version 2>/dev/null || true)
|
||||
version=$(echo "$version_raw" | sed -E 's/.*v?([0-9]+)\.([0-9]+)\.([0-9]+).*/\1.\2.\3/')
|
||||
|
||||
if [[ ! $version =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
IFS=. read -r major minor _ <<< "$version"
|
||||
|
||||
if ((major > 0)); then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ((minor >= 78)); then
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
function run_legacy_validate_inputs {
|
||||
local -a unit_dirs=()
|
||||
local final_exit_code=0
|
||||
local dir_path
|
||||
|
||||
while read -r dir_path; do
|
||||
if [[ -n $dir_path ]]; then
|
||||
unit_dirs+=("$dir_path")
|
||||
fi
|
||||
done < <(legacy_unit_dirs_from_files)
|
||||
|
||||
if [[ ${#unit_dirs[@]} -eq 0 ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# preserve errexit status
|
||||
shopt -qo errexit && ERREXIT_IS_SET=true
|
||||
set +e
|
||||
|
||||
for dir_path in "${unit_dirs[@]}"; do
|
||||
pushd "$dir_path" > /dev/null || continue
|
||||
terragrunt validate-inputs "${ARGS[@]}"
|
||||
|
||||
local exit_code=$?
|
||||
if [ $exit_code -ne 0 ]; then
|
||||
final_exit_code=$exit_code
|
||||
fi
|
||||
|
||||
popd > /dev/null
|
||||
done
|
||||
|
||||
[[ $ERREXIT_IS_SET ]] && set -e
|
||||
exit $final_exit_code
|
||||
}
|
||||
|
||||
function legacy_unit_dirs_from_files {
|
||||
local -a unit_files=()
|
||||
local file_with_path
|
||||
local file_dir
|
||||
local file_name
|
||||
|
||||
if common::is_hook_run_on_whole_repo "$HOOK_ID" "${FILES[@]}"; then
|
||||
find . -type f -name terragrunt.hcl \
|
||||
-not -path '*/.terragrunt-cache/*' \
|
||||
-not -path '*/.terraform/*' \
|
||||
| sort -u | while read -r unit_file; do
|
||||
dirname "$unit_file"
|
||||
done
|
||||
return
|
||||
fi
|
||||
|
||||
for file_with_path in "${FILES[@]}"; do
|
||||
file_dir=$(dirname "$file_with_path")
|
||||
file_name=$(basename "$file_with_path")
|
||||
|
||||
if [[ $file_name == terragrunt.hcl ]]; then
|
||||
unit_files+=("$file_with_path")
|
||||
continue
|
||||
fi
|
||||
|
||||
while read -r unit_file; do
|
||||
if [[ -n $unit_file ]]; then
|
||||
unit_files+=("$unit_file")
|
||||
fi
|
||||
done < <(find "$file_dir" -type f -name terragrunt.hcl \
|
||||
-not -path '*/.terragrunt-cache/*' \
|
||||
-not -path '*/.terraform/*' | sort -u)
|
||||
done
|
||||
|
||||
if [[ ${#unit_files[@]} -eq 0 ]]; then
|
||||
find . -type f -name terragrunt.hcl \
|
||||
-not -path '*/.terragrunt-cache/*' \
|
||||
-not -path '*/.terraform/*' \
|
||||
| sort -u | while read -r unit_file; do
|
||||
dirname "$unit_file"
|
||||
done
|
||||
return
|
||||
fi
|
||||
|
||||
printf '%s\n' "${unit_files[@]}" | sort -u | while read -r unit_file; do
|
||||
dirname "$unit_file"
|
||||
done
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
# Unique part of `common::per_dir_hook`. The function is executed in loop
|
||||
# on each provided dir path. Run wrapped tool with specified arguments
|
||||
# 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 {
|
||||
# shellcheck disable=SC2034 # Unused var.
|
||||
local -r dir_path="$1"
|
||||
# shellcheck disable=SC2034 # Unused var.
|
||||
local -r change_dir_in_unique_part="$2"
|
||||
shift 2
|
||||
local -a -r args=("$@")
|
||||
|
||||
# pass the arguments to hook
|
||||
terragrunt "${SUBCOMMAND[@]}" "${args[@]}"
|
||||
|
||||
# return exit code to common::per_dir_hook
|
||||
local exit_code=$?
|
||||
return $exit_code
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
# Unique part of `common::per_dir_hook`. The function is executed one time
|
||||
# in the root git repo
|
||||
# Arguments:
|
||||
# args (array) arguments that configure wrapped tool behavior
|
||||
#######################################################################
|
||||
function run_hook_on_whole_repo {
|
||||
local -a -r args=("$@")
|
||||
|
||||
# pass the arguments to hook
|
||||
terragrunt "${RUN_ALL_SUBCOMMAND[@]}" "${args[@]}"
|
||||
|
||||
# return exit code to common::per_dir_hook
|
||||
local exit_code=$?
|
||||
return $exit_code
|
||||
}
|
||||
|
||||
[ "${BASH_SOURCE[0]}" != "$0" ] || main "$@"
|
||||
Loading…
Add table
Add a link
Reference in a new issue