Compare commits

...

9 commits

Author SHA1 Message Date
semantic-release-bot
7067827cf3 chore(release): version 2.4.1 [skip ci]
## [2.4.1](https://github.com/tofuutils/pre-commit-opentofu/compare/v2.4.0...v2.4.1) (2026-06-01)

### Bug Fixes

* initialize config_file_no_color variable ([b766f82](b766f82b08))
* typo fixes ([3f263a9](3f263a968c))
* Update hadolint installation ([c9cd4a8](c9cd4a805d))
2026-06-01 21:40:06 +00:00
Michael Rosenfeld
9316d2989c refactor: improve directory handling and tofu validate logic
Deduplicate directories in tofu_docs_replace.py by using a set of real
paths, ensuring each directory is processed only once. Refactor
tofu_validate.sh to use command substitution with proper exit code
handling, improving reliability and clarity.

Signed-off-by: Michael Rosenfeld <michael@rosesecurity.com>
2026-06-01 22:39:41 +01:00
Michael Rosenfeld
b766f82b08 fix: initialize config_file_no_color variable
Initialize the config_file_no_color variable to prevent potential
unbound variable errors during script execution. This change improves
script robustness and reliability.

Signed-off-by: Michael Rosenfeld <michael@rosesecurity.com>
2026-06-01 22:39:41 +01:00
Michael Rosenfeld
3f263a968c fix: typo fixes
Some checks failed
Release / Release (push) Has been cancelled
Signed-off-by: Michael Rosenfeld <michael@rosesecurity.com>
2026-05-27 09:25:16 +01:00
Nikolai Mishin
c9cd4a805d fix: Update hadolint installation
Signed-off-by: Nikolai Mishin <sanduku.default@gmail.com>
2026-05-26 23:00:40 +01:00
semantic-release-bot
ab333d78c6 chore(release): version 2.4.0 [skip ci]
# [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](451aaa59b5))

### Features

* support .tofu file extension and update hooks/tools ([9624cc8](9624cc8f24))
2026-05-25 22:53:37 +00:00
kvendingoldo
fb62c63c56
Merge pull request #74 from RoseSecurity/support-tofu-file-extensions
feat: Support `.tofu` file extensions and pre-commit clean ups
2026-05-26 02:53:14 +04:00
Michael Rosenfeld
9624cc8f24
feat: support .tofu file extension and update hooks/tools
Add support for the `.tofu` file extension in OpenTofu config
matching and documentation. Update pre-commit hooks, regex, and
README to reflect support for `.tofu` files alongside `.tf` and
`.tfvars`. Fix minor shell quoting and array assignment issues.
Upgrade pre-commit-hooks to v6.0.0.

Signed-off-by: Michael Rosenfeld <michael@rosesecurity.com>
2026-05-24 22:06:26 -04:00
Michael Rosenfeld
451aaa59b5
fix: expand file extension patterns for tofu hooks
Update file matching patterns in .pre-commit-hooks.yaml to support .tofu,
.tfvars, and other relevant extensions for OpenTofu workflows. Also clarify
log message in tofu_wrapper_module_for_each.sh for missing files.

Signed-off-by: Michael Rosenfeld <michael@rosesecurity.com>
2026-05-24 22:06:26 -04:00
14 changed files with 77 additions and 41 deletions

View file

@ -98,4 +98,3 @@ jobs:
tags: | tags: |
registry.hub.docker.com/tofuutils/pre-commit-opentofu:nightly registry.hub.docker.com/tofuutils/pre-commit-opentofu:nightly
provenance: false provenance: false

View file

@ -27,9 +27,13 @@ jobs:
sudo apt update && sudo apt install shellcheck sudo apt update && sudo apt install shellcheck
- name: Install hadolint - name: Install hadolint
env:
GH_TOKEN: ${{ github.token }}
run: | 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 \ gh release download v2.14.0 --repo hadolint/hadolint --pattern "hadolint-linux-x86_64"
&& chmod +x hadolint && sudo mv hadolint /usr/bin/ mv hadolint-linux-x86_64 hadolint
chmod +x hadolint
sudo mv hadolint /usr/bin/
# Need to success pre-commit fix push # Need to success pre-commit fix push
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
with: with:

View file

@ -1,6 +1,6 @@
repos: repos:
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0 rev: v6.0.0
hooks: hooks:
# Git style # Git style
- id: check-added-large-files - id: check-added-large-files

View file

@ -4,7 +4,7 @@
entry: hooks/infracost_breakdown.sh entry: hooks/infracost_breakdown.sh
language: script language: script
require_serial: true require_serial: true
files: \.((tf|tofu)(vars)?|hcl)$ files: \.(tf|tofu|tfvars|hcl)$
exclude: \.terraform\/.*$ exclude: \.terraform\/.*$
- id: tofu_fmt - id: tofu_fmt
@ -12,7 +12,7 @@
description: Rewrites all OpenTofu configuration files to a canonical format. description: Rewrites all OpenTofu configuration files to a canonical format.
entry: hooks/tofu_fmt.sh entry: hooks/tofu_fmt.sh
language: script language: script
files: \.(tf|tofu)(vars)?$ files: \.(tf|tofu|tfvars|(tftest|tofutest|tfmock|tfquery)\.hcl)$
exclude: \.terraform\/.*$ exclude: \.terraform\/.*$
- id: tofu_docs - id: tofu_docs
@ -23,7 +23,7 @@
require_serial: true require_serial: true
entry: hooks/tofu_docs.sh entry: hooks/tofu_docs.sh
language: script language: script
files: (\.(tf|tofu)|\.terraform\.lock\.hcl)$ files: \.(tf|tofu|terraform\.lock\.hcl)$
exclude: \.terraform\/.*$ exclude: \.terraform\/.*$
- id: tofu_docs_without_aggregate_type_defaults - id: tofu_docs_without_aggregate_type_defaults
@ -52,7 +52,7 @@
require_serial: true require_serial: true
entry: hooks/tofu_validate.sh entry: hooks/tofu_validate.sh
language: script language: script
files: \.(tf|tofu)(vars)?$ files: \.(tf|tofu|tfvars|terraform\.lock\.hcl)$
exclude: \.terraform\/.*$ exclude: \.terraform\/.*$
- id: tofu_providers_lock - id: tofu_providers_lock
@ -70,7 +70,7 @@
require_serial: true require_serial: true
entry: hooks/tofu_tflint.sh entry: hooks/tofu_tflint.sh
language: script language: script
files: \.(tf|tofu)(vars)?$ files: \.(tf|tofu|tfvars)$
exclude: \.terraform\/.*$ exclude: \.terraform\/.*$
- id: terragrunt_fmt - id: terragrunt_fmt
@ -104,7 +104,7 @@
Static analysis of OpenTofu templates to spot potential security issues. Static analysis of OpenTofu templates to spot potential security issues.
require_serial: true require_serial: true
entry: hooks/tofu_tfsec.sh entry: hooks/tofu_tfsec.sh
files: \.(tf|tofu)(vars)?$ files: \.(tf|tofu|tfvars)$
language: script language: script
- id: tofu_trivy - id: tofu_trivy
@ -113,7 +113,7 @@
Static analysis of OpenTofu templates to spot potential security issues. Static analysis of OpenTofu templates to spot potential security issues.
require_serial: true require_serial: true
entry: hooks/tofu_trivy.sh entry: hooks/tofu_trivy.sh
files: \.(tf|tofu)(vars)?$ files: \.(tf|tofu|tfvars)$
language: script language: script
- id: checkov - id: checkov
@ -123,7 +123,7 @@
language: python language: python
pass_filenames: false pass_filenames: false
always_run: false always_run: false
files: \.tf$ files: \.(tf|tofu)$
exclude: \.terraform\/.*$ exclude: \.terraform\/.*$
require_serial: true require_serial: true
@ -145,7 +145,7 @@
pass_filenames: false pass_filenames: false
always_run: false always_run: false
require_serial: true require_serial: true
files: \.tf$ files: \.(tf|tofu)$
exclude: \.terraform\/.*$ exclude: \.terraform\/.*$
- id: terrascan - id: terrascan

View file

@ -2,6 +2,27 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## [2.4.1](https://github.com/tofuutils/pre-commit-opentofu/compare/v2.4.0...v2.4.1) (2026-06-01)
### Bug Fixes
* initialize config_file_no_color variable ([b766f82](https://github.com/tofuutils/pre-commit-opentofu/commit/b766f82b087793f75b3ffa61eb22da80031455ce))
* typo fixes ([3f263a9](https://github.com/tofuutils/pre-commit-opentofu/commit/3f263a968cd776924c3d1f4981e0fbf1cd79ff4d))
* Update hadolint installation ([c9cd4a8](https://github.com/tofuutils/pre-commit-opentofu/commit/c9cd4a805d378516a24dac33278fa51deb98bf2e))
# [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) # [2.3.0](https://github.com/tofuutils/pre-commit-opentofu/compare/v2.2.2...v2.3.0) (2026-04-21)
@ -36,7 +57,7 @@ All notable changes to this project will be documented in this file.
### Features ### 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) # [2.0.0](https://github.com/tofuutils/pre-commit-opentofu/compare/v1.0.4...v2.0.0) (2024-09-25)

View file

@ -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 \ 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 \ && 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/ \ && unzip tofu_${TOFU_VERSION}_${TARGETOS}_${TARGETARCH}.zip -d /usr/bin/ \
&& rm "tofu_${TOFU_VERSION}_${TARGETOS}_${TARGETARCH}.zip" \ && rm "tofu_${TOFU_VERSION}_${TARGETOS}_${TARGETARCH}.zip" \
&& rm "tofu_${TOFU_VERSION}_SHA256SUMS" && 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} ENV INFRACOST_SKIP_UPDATE_CHECK=${INFRACOST_SKIP_UPDATE_CHECK:-false}
ENTRYPOINT [ "/entrypoint.sh" ] ENTRYPOINT [ "/entrypoint.sh" ]

View file

@ -294,7 +294,7 @@ repos:
## Available Hooks ## 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:
<!-- markdownlint-disable no-inline-html --> <!-- markdownlint-disable no-inline-html -->
| Hook name | Description | Dependencies<br><sup>[Install instructions here](#1-install-dependencies)</sup> | | Hook name | Description | Dependencies<br><sup>[Install instructions here](#1-install-dependencies)</sup> |
@ -326,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). 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 hooks: Usage of environment variables in `--args`
> All, except deprecated hooks: `checkov`, `tofu_docs_replace` > All, except deprecated hooks: `checkov`, `tofu_docs_replace`
@ -716,7 +718,7 @@ To replicate functionality in `tofu_docs` hook:
- --args=--config=__GIT_WORKING_DIR__/.tflint.hcl - --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 ```yaml
- id: tofu_tflint - id: tofu_tflint
@ -930,7 +932,7 @@ To replicate functionality in `tofu_docs` hook:
require_serial: true require_serial: true
entry: .generate-providers.sh entry: .generate-providers.sh
language: script language: script
files: \.tf(vars)?$ files: \.(tf|tofu|tfvars)$
pass_filenames: false pass_filenames: false
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks

View file

@ -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. # `$arg` will be checked in `if` conditional, `$ARGS` will be used in the next functions.
# shellcheck disable=SC2016 # '${' should not be expanded # shellcheck disable=SC2016 # '${' should not be expanded
arg=${arg/'${'$env_var_name'}'/$env_var_value} arg=${arg/'${'$env_var_name'}'/$env_var_value}
ARGS[$arg_idx]=$arg ARGS[arg_idx]=$arg
# shellcheck disable=SC2016 # '${' should not be expanded # shellcheck disable=SC2016 # '${' should not be expanded
common::colorify "green" 'After ${'"$env_var_name"'} expansion: '"'$arg'\n" common::colorify "green" 'After ${'"$env_var_name"'} expansion: '"'$arg'\n"
continue continue

View file

@ -32,8 +32,8 @@ function normalize_validate_args_for_modern_terragrunt {
for arg_idx in "${!ARGS[@]}"; do for arg_idx in "${!ARGS[@]}"; do
case "${ARGS[$arg_idx]}" in case "${ARGS[$arg_idx]}" in
--terragrunt-strict-validate|--strict-validate) --terragrunt-strict-validate | --strict-validate)
ARGS[$arg_idx]="--strict" ARGS[arg_idx]="--strict"
;; ;;
esac esac
done done
@ -45,7 +45,7 @@ function terragrunt_version_ge_0_78 {
local major local major
local minor local minor
version_raw=$(terragrunt --version 2>/dev/null || true) 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/') 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 if [[ ! $version =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
@ -109,8 +109,8 @@ function legacy_unit_dirs_from_files {
if common::is_hook_run_on_whole_repo "$HOOK_ID" "${FILES[@]}"; then if common::is_hook_run_on_whole_repo "$HOOK_ID" "${FILES[@]}"; then
find . -type f -name terragrunt.hcl \ find . -type f -name terragrunt.hcl \
-not -path '*/.terragrunt-cache/*' \ -not -path '*/.terragrunt-cache/*' \
-not -path '*/.terraform/*' \ -not -path '*/.terraform/*' |
| sort -u | while read -r unit_file; do sort -u | while read -r unit_file; do
dirname "$unit_file" dirname "$unit_file"
done done
return return
@ -137,8 +137,8 @@ function legacy_unit_dirs_from_files {
if [[ ${#unit_files[@]} -eq 0 ]]; then if [[ ${#unit_files[@]} -eq 0 ]]; then
find . -type f -name terragrunt.hcl \ find . -type f -name terragrunt.hcl \
-not -path '*/.terragrunt-cache/*' \ -not -path '*/.terragrunt-cache/*' \
-not -path '*/.terraform/*' \ -not -path '*/.terraform/*' |
| sort -u | while read -r unit_file; do sort -u | while read -r unit_file; do
dirname "$unit_file" dirname "$unit_file"
done done
return return

View file

@ -69,6 +69,7 @@ function tofu_docs {
local -a -r files=("$@") local -a -r files=("$@")
local -a paths local -a paths
local config_file_no_color=""
local index=0 local index=0
local file_with_path local file_with_path

View file

@ -36,13 +36,14 @@ def main(argv=None):
args = parser.parse_args(argv) args = parser.parse_args(argv)
dirs = [] dirs = []
seen_dirs = set()
for filename in args.filenames: for filename in args.filenames:
if os.path.realpath(filename) not in dirs and ( if filename.endswith((".tf", ".tofu", ".tfvars")):
filename.endswith(".tf") dir_path = os.path.dirname(filename)
or filename.endswith(".tofu") dir_key = os.path.realpath(dir_path)
or filename.endswith(".tfvars") if dir_key not in seen_dirs:
): seen_dirs.add(dir_key)
dirs.append(os.path.dirname(filename)) dirs.append(dir_path)
retval = 0 retval = 0

View file

@ -128,12 +128,18 @@ function per_dir_hook_unique_part {
if [ "$retry_once_with_cleanup" != "true" ]; then if [ "$retry_once_with_cleanup" != "true" ]; then
# tofu validate only # tofu validate only
validate_output=$(tofu validate "${args[@]}" 2>&1) if validate_output=$(tofu validate "${args[@]}" 2>&1); then
exit_code=0
else
exit_code=$? exit_code=$?
fi
else else
# tofu validate, plus capture possible errors # tofu validate, plus capture possible errors
validate_output=$(tofu validate -json "${args[@]}" 2>&1) if validate_output=$(tofu validate -json "${args[@]}" 2>&1); then
exit_code=0
else
exit_code=$? exit_code=$?
fi
# Match specific validation errors # Match specific validation errors
local -i validate_errors_matched local -i validate_errors_matched
@ -155,10 +161,13 @@ function per_dir_hook_unique_part {
return $exit_code return $exit_code
} }
validate_output=$(tofu validate "${args[@]}" 2>&1) if validate_output=$(tofu validate "${args[@]}" 2>&1); then
exit_code=0
else
exit_code=$? exit_code=$?
fi fi
fi fi
fi
if [ $exit_code -ne 0 ]; then if [ $exit_code -ne 0 ]; then
common::colorify "red" "Validation failed: $dir_path" common::colorify "red" "Validation failed: $dir_path"

View file

@ -315,7 +315,7 @@ EOF
all_tf_content=$(find "${full_module_dir}" -regex '.*\.(tf|tofu)' -maxdepth 1 -type f -exec cat {} +) all_tf_content=$(find "${full_module_dir}" -regex '.*\.(tf|tofu)' -maxdepth 1 -type f -exec cat {} +)
if [[ ! $all_tf_content ]]; then 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 continue
fi fi

View file

@ -360,7 +360,7 @@ getopt() {
} }
_getopt_resolve_abbrev() { _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 # If the abbreviation is unambiguous, echoes the expansion on stdout
# and returns 0. If the abbreviation is ambiguous, prints a message on # and returns 0. If the abbreviation is ambiguous, prints a message on
# stderr and returns 1. (For first parse this should convert to exit # stderr and returns 1. (For first parse this should convert to exit