diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..c6ee553
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,10 @@
+---
+version: 2
+updates:
+ - package-ecosystem: "github-actions"
+ directory: /
+ schedule:
+ interval: daily
+ time: "11:00"
+ commit-message:
+ prefix: "gh-actions:"
diff --git a/.github/workflows/build-image-test.yaml b/.github/workflows/build-image-test.yaml
index f83e11e..c7223f0 100644
--- a/.github/workflows/build-image-test.yaml
+++ b/.github/workflows/build-image-test.yaml
@@ -9,13 +9,13 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
with:
fetch-depth: 0
- name: Get changed Dockerfile
id: changed-files-specific
- uses: tj-actions/changed-files@2c85495a7bb72f2734cb5181e29b2ee5e08e61f7 # v13.1
+ uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
with:
files: |
Dockerfile
@@ -52,7 +52,7 @@ jobs:
- name: Dive - check image for waste files
if: steps.changed-files-specific.outputs.any_changed == 'true'
- uses: MaxymVlasov/dive-action@0035999cae50d4ef657ac94be84f01812aa192a5 # v0.1.0
+ uses: MaxymVlasov/dive-action@fafb796951b322cc4926b8a5eafda89ab9de8edf # v1.5.1
with:
image: ghcr.io/${{ github.repository }}:${{ env.IMAGE_TAG }}
config-file: ${{ github.workspace }}/.github/.dive-ci.yaml
diff --git a/.github/workflows/build-image.yaml b/.github/workflows/build-image.yaml
index 2d0c497..a79e218 100644
--- a/.github/workflows/build-image.yaml
+++ b/.github/workflows/build-image.yaml
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@v4
+ uses: actions/checkout@v5.0.1
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
@@ -22,7 +22,7 @@ jobs:
uses: docker/setup-buildx-action@v3
- name: Login to ghcr.io
- uses: docker/login-action@v3
+ uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
@@ -66,7 +66,7 @@ jobs:
"github_token=${{ secrets.GITHUB_TOKEN }}"
- name: Login to DockerHub Container Registry
- uses: docker/login-action@v3
+ uses: docker/login-action@v4
with:
registry: registry.hub.docker.com
username: ${{ secrets.DOCKERHUB_USER }}
@@ -98,4 +98,3 @@ jobs:
tags: |
registry.hub.docker.com/tofuutils/pre-commit-opentofu:nightly
provenance: false
-
diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml
index 7a8b642..3d86d51 100644
--- a/.github/workflows/pr-title.yml
+++ b/.github/workflows/pr-title.yml
@@ -14,7 +14,7 @@ jobs:
steps:
# Please look up the latest version from
# https://github.com/amannn/action-semantic-pull-request/releases
- - uses: amannn/action-semantic-pull-request@e9fabac35e210fea40ca5b14c0da95a099eff26f # v5.4.0
+ - uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml
index 1085384..b0566cf 100644
--- a/.github/workflows/pre-commit.yaml
+++ b/.github/workflows/pre-commit.yaml
@@ -6,7 +6,7 @@ jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- run: |
git fetch --no-tags --prune --depth=1 origin +refs/heads/*:refs/remotes/origin/*
@@ -27,20 +27,24 @@ jobs:
sudo apt update && sudo apt install shellcheck
- name: Install hadolint
+ env:
+ GH_TOKEN: ${{ github.token }}
run: |
- curl -L "$(curl -s https://api.github.com/repos/hadolint/hadolint/releases/latest | grep -o -E -m 1 "https://.+?/hadolint-Linux-x86_64")" > hadolint \
- && chmod +x hadolint && sudo mv hadolint /usr/bin/
+ gh release download v2.14.0 --repo hadolint/hadolint --pattern "hadolint-linux-x86_64"
+ mv hadolint-linux-x86_64 hadolint
+ chmod +x hadolint
+ sudo mv hadolint /usr/bin/
# Need to success pre-commit fix push
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha }}
# Skip tofu_tflint which interferes to commit pre-commit auto-fixes
- - uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0
+ - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
with:
python-version: '3.9'
- name: Execute pre-commit
- uses: pre-commit/action@9b88afc9cd57fd75b655d5c71bd38146d07135fe # v2.0.3
+ uses: pre-commit/action@576ff52938d158a24ac7e009dfa94b1455e7df99
env:
SKIP: no-commit-to-branch,hadolint
with:
@@ -49,7 +53,7 @@ jobs:
# Run only skipped checks
- name: Execute pre-commit check that have no auto-fixes
if: always()
- uses: pre-commit/action@9b88afc9cd57fd75b655d5c71bd38146d07135fe # v2.0.3
+ uses: pre-commit/action@576ff52938d158a24ac7e009dfa94b1455e7df99
env:
SKIP: check-added-large-files,check-merge-conflict,check-vcs-permalinks,forbid-new-submodules,no-commit-to-branch,end-of-file-fixer,trailing-whitespace,check-yaml,check-merge-conflict,check-executables-have-shebangs,check-case-conflict,mixed-line-ending,detect-aws-credentials,detect-private-key,shfmt,shellcheck
with:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 4aa0df1..35b8f62 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -18,13 +18,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
with:
persist-credentials: false
fetch-depth: 0
- name: Release
- uses: cycjimmy/semantic-release-action@61680d0e9b02ff86f5648ade99e01be17f0260a4 # v4.0.0
+ uses: cycjimmy/semantic-release-action@b12c8f6015dc215fe37bc154d4ad456dd3833c90 # v6.0.0
with:
semantic_version: 18.0.0
extra_plugins: |
diff --git a/.github/workflows/stale-actions.yaml b/.github/workflows/stale-actions.yaml
index 32f08e7..d797c08 100644
--- a/.github/workflows/stale-actions.yaml
+++ b/.github/workflows/stale-actions.yaml
@@ -7,7 +7,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- - uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0
+ - uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
# Staling issues and PR's
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 17fc5a6..e801df2 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v4.5.0
+ rev: v6.0.0
hooks:
# Git style
- id: check-added-large-files
diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml
index c5c5f66..d60a3e5 100644
--- a/.pre-commit-hooks.yaml
+++ b/.pre-commit-hooks.yaml
@@ -4,7 +4,7 @@
entry: hooks/infracost_breakdown.sh
language: script
require_serial: true
- files: \.((tf|tofu)(vars)?|hcl)$
+ files: \.(tf|tofu|tfvars|hcl)$
exclude: \.terraform\/.*$
- id: tofu_fmt
@@ -12,7 +12,7 @@
description: Rewrites all OpenTofu configuration files to a canonical format.
entry: hooks/tofu_fmt.sh
language: script
- files: \.(tf|tofu)(vars)?$
+ files: \.(tf|tofu|tfvars|(tftest|tofutest|tfmock|tfquery)\.hcl)$
exclude: \.terraform\/.*$
- id: tofu_docs
@@ -23,7 +23,7 @@
require_serial: true
entry: hooks/tofu_docs.sh
language: script
- files: (\.(tf|tofu)|\.terraform\.lock\.hcl)$
+ files: \.(tf|tofu|terraform\.lock\.hcl)$
exclude: \.terraform\/.*$
- id: tofu_docs_without_aggregate_type_defaults
@@ -52,7 +52,7 @@
require_serial: true
entry: hooks/tofu_validate.sh
language: script
- files: \.(tf|tofu)(vars)?$
+ files: \.(tf|tofu|tfvars|terraform\.lock\.hcl)$
exclude: \.terraform\/.*$
- id: tofu_providers_lock
@@ -70,7 +70,7 @@
require_serial: true
entry: hooks/tofu_tflint.sh
language: script
- files: \.(tf|tofu)(vars)?$
+ files: \.(tf|tofu|tfvars)$
exclude: \.terraform\/.*$
- id: terragrunt_fmt
@@ -90,13 +90,21 @@
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:
Static analysis of OpenTofu templates to spot potential security issues.
require_serial: true
entry: hooks/tofu_tfsec.sh
- files: \.(tf|tofu)(vars)?$
+ files: \.(tf|tofu|tfvars)$
language: script
- id: tofu_trivy
@@ -105,7 +113,7 @@
Static analysis of OpenTofu templates to spot potential security issues.
require_serial: true
entry: hooks/tofu_trivy.sh
- files: \.(tf|tofu)(vars)?$
+ files: \.(tf|tofu|tfvars)$
language: script
- id: checkov
@@ -115,7 +123,7 @@
language: python
pass_filenames: false
always_run: false
- files: \.tf$
+ files: \.(tf|tofu)$
exclude: \.terraform\/.*$
require_serial: true
@@ -137,7 +145,7 @@
pass_filenames: false
always_run: false
require_serial: true
- files: \.tf$
+ files: \.(tf|tofu)$
exclude: \.terraform\/.*$
- id: terrascan
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5bbab38..2112ef2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,40 @@
All notable changes to this project will be documented in this file.
+# [2.4.0](https://github.com/tofuutils/pre-commit-opentofu/compare/v2.3.0...v2.4.0) (2026-05-25)
+
+
+### Bug Fixes
+
+* expand file extension patterns for tofu hooks ([451aaa5](https://github.com/tofuutils/pre-commit-opentofu/commit/451aaa59b552eb3913629c835af8b6b568aab120))
+
+
+### Features
+
+* support .tofu file extension and update hooks/tools ([9624cc8](https://github.com/tofuutils/pre-commit-opentofu/commit/9624cc8f24177378449203194b4f0ee71bc6c1a0))
+
+# [2.3.0](https://github.com/tofuutils/pre-commit-opentofu/compare/v2.2.2...v2.3.0) (2026-04-21)
+
+
+### Features
+
+* add terragrunt_validate_inputs hook ([0ca9eca](https://github.com/tofuutils/pre-commit-opentofu/commit/0ca9eca3823420f31d09c62bf1672bea03ca3e07))
+
+## [2.2.2](https://github.com/tofuutils/pre-commit-opentofu/compare/v2.2.1...v2.2.2) (2025-10-22)
+
+
+### Bug Fixes
+
+* replace deprecated hclfmt with hcl format command ([f1a589b](https://github.com/tofuutils/pre-commit-opentofu/commit/f1a589bd124b277cc02fcbf04ee05017fb8822c0))
+
+## [2.2.1](https://github.com/tofuutils/pre-commit-opentofu/compare/v2.2.0...v2.2.1) (2025-06-04)
+
+
+### Bug Fixes
+
+* make infracost_breakdown.sh compatible with bash 3.2 (macOS) ([df886fa](https://github.com/tofuutils/pre-commit-opentofu/commit/df886fa772e7d1eedf5603327c0cf02968e7d779))
+* Update pre-commit/action version ([#30](https://github.com/tofuutils/pre-commit-opentofu/issues/30)) ([44c7b5d](https://github.com/tofuutils/pre-commit-opentofu/commit/44c7b5dec9362d2fe7ed5e8786f4d95956791d3d))
+
# [2.2.0](https://github.com/tofuutils/pre-commit-opentofu/compare/v2.1.0...v2.2.0) (2025-03-29)
@@ -14,7 +48,7 @@ All notable changes to this project will be documented in this file.
### Features
-* spport .tofu files ([#6](https://github.com/tofuutils/pre-commit-opentofu/issues/6)) ([e059c58](https://github.com/tofuutils/pre-commit-opentofu/commit/e059c5859bceddf1ca018f55851f6940ad51f1c2))
+* support .tofu files ([#6](https://github.com/tofuutils/pre-commit-opentofu/issues/6)) ([e059c58](https://github.com/tofuutils/pre-commit-opentofu/commit/e059c5859bceddf1ca018f55851f6940ad51f1c2))
# [2.0.0](https://github.com/tofuutils/pre-commit-opentofu/compare/v1.0.4...v2.0.0) (2024-09-25)
diff --git a/Dockerfile b/Dockerfile
index 1616bfd..6699cb2 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -22,7 +22,7 @@ RUN [ ${PRE_COMMIT_VERSION} = "latest" ] && pip3 install --no-cache-dir pre-comm
RUN curl -LO https://github.com/opentofu/opentofu/releases/download/v${TOFU_VERSION}/tofu_${TOFU_VERSION}_${TARGETOS}_${TARGETARCH}.zip \
&& curl -LO https://github.com/opentofu/opentofu/releases/download/v${TOFU_VERSION}/tofu_${TOFU_VERSION}_SHA256SUMS \
- && [ $(sha256sum "tofu_${TOFU_VERSION}_${TARGETOS}_${TARGETARCH}.zip" | cut -f 1 -d ' ') = "$(grep "tofu_${TOFU_VERSION}_${TARGETOS}_${TARGETARCH}.zip" tofu_*_SHA256SUMS | cut -f 1 -d ' ')" ] \
+ && [ "$(sha256sum "tofu_${TOFU_VERSION}_${TARGETOS}_${TARGETARCH}.zip" | cut -f 1 -d ' ')" = "$(grep "tofu_${TOFU_VERSION}_${TARGETOS}_${TARGETARCH}.zip" tofu_*_SHA256SUMS | cut -f 1 -d ' ')" ] \
&& unzip tofu_${TOFU_VERSION}_${TARGETOS}_${TARGETARCH}.zip -d /usr/bin/ \
&& rm "tofu_${TOFU_VERSION}_${TARGETOS}_${TARGETARCH}.zip" \
&& rm "tofu_${TOFU_VERSION}_SHA256SUMS"
@@ -235,4 +235,3 @@ ENV INFRACOST_API_KEY=${INFRACOST_API_KEY:-}
ENV INFRACOST_SKIP_UPDATE_CHECK=${INFRACOST_SKIP_UPDATE_CHECK:-false}
ENTRYPOINT [ "/entrypoint.sh" ]
-
diff --git a/README.md b/README.md
index 73c4bb0..c8434c9 100644
--- a/README.md
+++ b/README.md
@@ -60,7 +60,7 @@ If you are using `pre-commit-opentofu` already or want to support its developmen
* [`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,9 +266,35 @@ 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: # 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:
+There are several [pre-commit](https://pre-commit.com/) hooks to keep OpenTofu configurations (`*.tf`, `*.tofu`, and `*.tfvars`) and Terragrunt configurations (`*.hcl`) in a good shape:
| Hook name | Description | Dependencies
[Install instructions here](#1-install-dependencies) |
@@ -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` |
@@ -299,6 +326,8 @@ Check the [source file](https://github.com/tofuutils/pre-commit-opentofu/blob/ma
OpenTofu operates on a per-dir basis, while `pre-commit` framework only supports files and files that exist. This means if you only remove the TF-related file without any other changes in the same dir, checks will be skipped. Example and details [here](https://github.com/pre-commit/pre-commit/issues/3048).
+Hooks match `*.tofu` files where OpenTofu configuration files are supported, but some wrapped third-party tools may lag behind OpenTofu's native `*.tofu` parsing. If a hook runs `terraform-docs`, `tflint`, `tfsec`, `trivy`, `checkov`, `infracost`, or `tfupdate`, make sure the installed tool version supports the file extensions used in your repository.
+
### All hooks: Usage of environment variables in `--args`
> All, except deprecated hooks: `checkov`, `tofu_docs_replace`
@@ -552,7 +581,7 @@ To replicate functionality in `tofu_docs` hook:
- --args=--config=.terraform-docs.yml
```
-### terraftofu_fmtorm_fmt
+### tofu_fmt
1. `tofu_fmt` supports custom arguments so you can pass [supported flags](https://www.terraform.io/docs/cli/commands/fmt.html#usage). Eg:
@@ -689,7 +718,7 @@ To replicate functionality in `tofu_docs` hook:
- --args=--config=__GIT_WORKING_DIR__/.tflint.hcl
```
-3. By default, pre-commit-opentofu performs directory switching into the OpenTofu 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:
+3. By default, pre-commit-opentofu performs directory switching into the OpenTofu modules for you. If you want to delegate 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: tofu_tflint
@@ -903,7 +932,7 @@ To replicate functionality in `tofu_docs` hook:
require_serial: true
entry: .generate-providers.sh
language: script
- files: \.tf(vars)?$
+ files: \.(tf|tofu|tfvars)$
pass_filenames: false
- repo: https://github.com/pre-commit/pre-commit-hooks
diff --git a/hooks/_common.sh b/hooks/_common.sh
index 133e457..5808c19 100644
--- a/hooks/_common.sh
+++ b/hooks/_common.sh
@@ -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%%\}*}
@@ -123,7 +123,7 @@ function common::parse_and_export_env_vars {
# `$arg` will be checked in `if` conditional, `$ARGS` will be used in the next functions.
# shellcheck disable=SC2016 # '${' should not be expanded
arg=${arg/'${'$env_var_name'}'/$env_var_value}
- ARGS[$arg_idx]=$arg
+ ARGS[arg_idx]=$arg
# shellcheck disable=SC2016 # '${' should not be expanded
common::colorify "green" 'After ${'"$env_var_name"'} expansion: '"'$arg'\n"
continue
diff --git a/hooks/infracost_breakdown.sh b/hooks/infracost_breakdown.sh
index 5515791..121f49b 100755
--- a/hooks/infracost_breakdown.sh
+++ b/hooks/infracost_breakdown.sh
@@ -70,19 +70,24 @@ function infracost_breakdown_ {
# -h .totalHourlyCost > 0.1
# --hook-config=.currency == "USD"
first_char=${check:0:1}
- last_char=${check: -1}
+ last_char=${check:$((${#check} - 1)):1}
if [ "$first_char" == "$last_char" ] && {
[ "$first_char" == '"' ] || [ "$first_char" == "'" ]
}; then
- check="${check:1:-1}"
+ check="${check:1:$((${#check} - 2))}"
fi
- mapfile -t operations < <(echo "$check" | grep -oE '[!<>=]{1,2}')
+ # Replace mapfile with while read loop for bash 3.2 compatibility
+ operations=()
+ while IFS= read -r line; do
+ operations+=("$line")
+ done < <(echo "$check" | grep -oE '[!<>=]{1,2}')
+
# Get the very last operator, that is used in comparison inside `jq` query.
# From the example below we need to pick the `>` which is in between `add` and `1000`,
# but not the `!=`, which goes earlier in the `jq` expression
# [.projects[].diff.totalMonthlyCost | select (.!=null) | tonumber] | add > 1000
- operation=${operations[-1]}
+ operation=${operations[$((${#operations[@]} - 1))]}
IFS="$operation" read -r -a jq_check <<< "$check"
real_value="$(jq "${jq_check[0]}" <<< "$RESULTS")"
diff --git a/hooks/terragrunt_fmt.sh b/hooks/terragrunt_fmt.sh
index 7c78b92..0b4165a 100755
--- a/hooks/terragrunt_fmt.sh
+++ b/hooks/terragrunt_fmt.sh
@@ -12,7 +12,7 @@ function main {
common::parse_cmdline "$@"
common::export_provided_env_vars "${ENV_VARS[@]}"
common::parse_and_export_env_vars
- # JFYI: terragrunt hclfmt color already suppressed via PRE_COMMIT_COLOR=never
+ # JFYI: terragrunt hcl format color already suppressed via PRE_COMMIT_COLOR=never
# shellcheck disable=SC2153 # False positive
common::per_dir_hook "$HOOK_ID" "${#ARGS[@]}" "${ARGS[@]}" "${FILES[@]}"
@@ -40,7 +40,7 @@ function per_dir_hook_unique_part {
local -a -r args=("$@")
# pass the arguments to hook
- terragrunt hclfmt "${args[@]}"
+ terragrunt hcl format "${args[@]}"
# return exit code to common::per_dir_hook
local exit_code=$?
@@ -57,7 +57,7 @@ function run_hook_on_whole_repo {
local -a -r args=("$@")
# pass the arguments to hook
- terragrunt hclfmt "$(pwd)" "${args[@]}"
+ terragrunt hcl format "$(pwd)" "${args[@]}"
# return exit code to common::per_dir_hook
local exit_code=$?
diff --git a/hooks/terragrunt_validate_inputs.sh b/hooks/terragrunt_validate_inputs.sh
new file mode 100755
index 0000000..a406324
--- /dev/null
+++ b/hooks/terragrunt_validate_inputs.sh
@@ -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 "$@"
diff --git a/hooks/tofu_wrapper_module_for_each.sh b/hooks/tofu_wrapper_module_for_each.sh
index c87db22..4572b58 100755
--- a/hooks/tofu_wrapper_module_for_each.sh
+++ b/hooks/tofu_wrapper_module_for_each.sh
@@ -315,7 +315,7 @@ EOF
all_tf_content=$(find "${full_module_dir}" -regex '.*\.(tf|tofu)' -maxdepth 1 -type f -exec cat {} +)
if [[ ! $all_tf_content ]]; then
- common::colorify "yellow" "Skipping ${full_module_dir} because there are no *.(tf|tofu) files."
+ common::colorify "yellow" "Skipping ${full_module_dir} because there are no .tf or .tofu files."
continue
fi
diff --git a/lib_getopt b/lib_getopt
index c4b21fa..74e72f1 100644
--- a/lib_getopt
+++ b/lib_getopt
@@ -360,7 +360,7 @@ getopt() {
}
_getopt_resolve_abbrev() {
- # Resolves an abbrevation from a list of possibilities.
+ # Resolves an abbreviation from a list of possibilities.
# If the abbreviation is unambiguous, echoes the expansion on stdout
# and returns 0. If the abbreviation is ambiguous, prints a message on
# stderr and returns 1. (For first parse this should convert to exit