Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
869f6b6
chore(AI): Init OpenSpec skills, wire into AGENTS.md
MaxymVlasov Jul 23, 2026
3707561
f
MaxymVlasov Jul 23, 2026
0051ab7
Merge branch 'master' into openspec
MaxymVlasov Jul 24, 2026
f053b65
commit part that makes +- sense
MaxymVlasov Jul 23, 2026
3df47b5
And commit a second part
MaxymVlasov Jul 23, 2026
c95abe6
Archive spec
MaxymVlasov Jul 23, 2026
e24e21b
Drop extralinks, as #1003 discarded
MaxymVlasov Jul 24, 2026
4c23c39
Apply suggestions from code review - docs
MaxymVlasov Jul 28, 2026
2e303f6
Rename function resolve_tool_version to resolve_tool_path
MaxymVlasov Jul 28, 2026
2764a0e
Apply suggestions from code review - code, 1st round
MaxymVlasov Jul 28, 2026
c915e7b
Apply suggestions from code review
MaxymVlasov Jul 28, 2026
b0bc55b
Apply review suggestions and little improvements
MaxymVlasov Jul 28, 2026
a5e62cc
chore: untrack local openspec.ai tooling files
MaxymVlasov Jul 28, 2026
4438ee8
Still ask AI to generate and use openspec, but do not commit skills
MaxymVlasov Jul 28, 2026
b85f444
fix(hooks): stop masking tool_path resolution failures
MaxymVlasov Jul 28, 2026
a432cff
revert: drop OpenSpec-related changes, moved to #1005
MaxymVlasov Jul 28, 2026
a475d47
fix(hooks): check terragrunt syntax version against tool_path, not $PATH
MaxymVlasov Jul 28, 2026
4dfc05c
Merge branch 'master' into downloadable_and_version_controlled_hooks
MaxymVlasov Jul 28, 2026
243e6f9
Apply suggestion from @MaxymVlasov
MaxymVlasov Jul 28, 2026
1c881c1
Add table from PR review comments
MaxymVlasov Jul 28, 2026
2ed31af
Fix numbering
MaxymVlasov Jul 28, 2026
cc08a68
Merge branch 'master' into downloadable_and_version_controlled_hooks
MaxymVlasov Aug 12, 2026
ccb0e11
feat(tests): Cover `--hook-config=--tool-version` resolution with pyt…
MaxymVlasov Aug 18, 2026
6078e48
Merge branch 'master' into downloadable_and_version_controlled_hooks
MaxymVlasov Aug 18, 2026
43bf759
fix(hooks): Make cache-miss download race-safe (#1012)
MaxymVlasov Aug 20, 2026
dd02688
Update tools/install/_common.sh
MaxymVlasov Aug 21, 2026
7892df6
fix(tool-version): Address Copilot review feedback
MaxymVlasov Aug 21, 2026
277e7cd
chore(docker): Silence hadolint DL3064 false positives
MaxymVlasov Aug 21, 2026
fb8c3f8
fix(ci): wire GITHUB_TOKEN into docker image builds
MaxymVlasov Aug 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ per-file-ignores =
WPS226,
# WPS115: "Require snake_case for naming class attributes" -- testing legitimate case, ignored in main code
WPS115,
tests/pytest/tool_version_test.py:
# WPS226: "Forbid the overuse of string literals" -- same legitimate test-fixture rationale as _cli_test.py above
WPS226,
# WPS202: "Found too many module members" -- this suite covers one resolution branch per test, so the module is intentionally wide rather than deep
WPS202,
# WPS204: "Found overused expression" -- every test wires the same hermetic env/PATH sandbox and re-asserts "no download happened"; deduplicating that into helpers would hide what each test actually guarantees
WPS204,
# We will not spend time on fixing complexity in deprecated hook
src/pre_commit_terraform/terraform_docs_replace.py: WPS232

Expand Down
3 changes: 3 additions & 0 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ You can use [this PR](https://github.com/antonbabenko/pre-commit-terraform/pull/
* `docker build -t pre-commit --build-arg INSTALL_ALL=true .`
* `docker build -t pre-commit --build-arg <NEW_HOOK>_VERSION=latest .`
* `docker build -t pre-commit --build-arg <NEW_HOOK>_VERSION=<1.2.3> .`

> [!NOTE]
> `tools/install/<tool>.sh` has a second call site besides the Dockerfile: if the new tool is distributed as a GitHub (or HashiCorp) release binary, `hooks/_common.sh::common::resolve_tool_path` invokes this same script at hook run-time to support [`--hook-config=--tool-version=`](../README.md#most-hooks-pin-a-specific-tool-version). Wire your new hook to it the same way the existing binary-wrapping hooks are (look at how `terraform_tflint.sh` or `terrascan.sh` call `common::resolve_tool_path`), unless the tool has a different distribution model (e.g. pip, like `checkov`) - in that case, skip this and leave a comment explaining why, as `terraform_checkov.sh` does.
2. Add Docker structure tests to [`.github/.container-structure-test-config.yaml`](.container-structure-test-config.yaml)
3. Add new hook to [`.pre-commit-hooks.yaml`](../.pre-commit-hooks.yaml)
4. Create hook file. Don't forget to make it executable via `chmod +x /path/to/hook/file`.
Expand Down
6 changes: 2 additions & 4 deletions .github/workflows/build-image-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,12 @@ jobs:
context: .
build-args: |
INSTALL_ALL=true
GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}
push: false
load: true
tags: ${{ env.IMAGE }}
# Fix multi-platform: https://github.com/docker/buildx/issues/1533
provenance: false
secrets: |
"github_token=${{ secrets.GITHUB_TOKEN }}"

- name: Setup Container Structure Tests
if: steps.changed-files-specific.outputs.any_changed == 'true'
Expand Down Expand Up @@ -121,10 +120,9 @@ jobs:
context: .
build-args: |
INSTALL_ALL=true
GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}
platforms: linux/amd64,linux/arm64
push: false
tags: ${{ env.IMAGE }}
# Fix multi-platform: https://github.com/docker/buildx/issues/1533
provenance: false
secrets: |
"github_token=${{ secrets.GITHUB_TOKEN }}"
6 changes: 2 additions & 4 deletions .github/workflows/build-image.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,14 @@ jobs:
context: .
build-args: |
INSTALL_ALL=true
GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}
platforms: linux/amd64,linux/arm64
push: true
tags: |
${{ env.IMAGE_REPO }}:${{ env.IMAGE_TAG }}
${{ env.IMAGE_REPO }}:latest
# Fix multi-platform: https://github.com/docker/buildx/issues/1533
provenance: false
secrets: |
"github_token=${{ secrets.GITHUB_TOKEN }}"

- name: Build and Push nightly
if: github.event_name == 'schedule'
Expand All @@ -75,11 +74,10 @@ jobs:
context: .
build-args: |
INSTALL_ALL=true
GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}
platforms: linux/amd64,linux/arm64
push: true
tags: |
${{ env.IMAGE_REPO }}:nightly
# Fix multi-platform: https://github.com/docker/buildx/issues/1533
provenance: false
secrets: |
"github_token=${{ secrets.GITHUB_TOKEN }}"
7 changes: 7 additions & 0 deletions .github/workflows/reusable-tox.yml
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,13 @@ jobs:
id: tox-run
shell: bash
env:
# Consumed by tools/install/_common.sh (via `pass_env = GITHUB_*`
# in tox.ini) to authenticate GitHub API calls made while
# resolving `--tool-version` pins - without it, requests are
# unauthenticated and share GitHub's low per-IP rate limit
# across every job in this workflow's matrix, which is exactly
# what test_real_download_on_cache_miss can hit.
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Comment thread
MaxymVlasov marked this conversation as resolved.
INPUT_TOX_RUN_POSARGS: ${{ inputs.tox-run-posargs }}
run: |-
tox_common_args=(
Expand Down
6 changes: 6 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#
# Install required tools
#
ARG PRE_COMMIT_VERSION=${PRE_COMMIT_VERSION:-latest}

Check warning on line 23 in Dockerfile

View workflow job for this annotation

GitHub Actions / Ubuntu ARM

Variables should be defined before their use

UndefinedVar: Usage of undefined variable '$PRE_COMMIT_VERSION' More info: https://docs.docker.com/go/dockerfile/rule/undefined-var/

Check warning on line 23 in Dockerfile

View workflow job for this annotation

GitHub Actions / Ubuntu x64

Variables should be defined before their use

UndefinedVar: Usage of undefined variable '$PRE_COMMIT_VERSION' More info: https://docs.docker.com/go/dockerfile/rule/undefined-var/
RUN touch /.env && \
if [ "$PRE_COMMIT_VERSION" = "false" ]; then \
echo "Vital software can't be skipped" && exit 1; \
Expand All @@ -30,19 +30,19 @@
#
# Install tools
#
ARG OPENTOFU_VERSION=${OPENTOFU_VERSION:-false}

Check warning on line 33 in Dockerfile

View workflow job for this annotation

GitHub Actions / Ubuntu ARM

Variables should be defined before their use

UndefinedVar: Usage of undefined variable '$OPENTOFU_VERSION' More info: https://docs.docker.com/go/dockerfile/rule/undefined-var/

Check warning on line 33 in Dockerfile

View workflow job for this annotation

GitHub Actions / Ubuntu x64

Variables should be defined before their use

UndefinedVar: Usage of undefined variable '$OPENTOFU_VERSION' More info: https://docs.docker.com/go/dockerfile/rule/undefined-var/
ARG TERRAFORM_VERSION=${TERRAFORM_VERSION:-false}

Check warning on line 34 in Dockerfile

View workflow job for this annotation

GitHub Actions / Ubuntu ARM

Variables should be defined before their use

UndefinedVar: Usage of undefined variable '$TERRAFORM_VERSION' More info: https://docs.docker.com/go/dockerfile/rule/undefined-var/

ARG CHECKOV_VERSION=${CHECKOV_VERSION:-false}

Check warning on line 36 in Dockerfile

View workflow job for this annotation

GitHub Actions / Ubuntu x64

Variables should be defined before their use

UndefinedVar: Usage of undefined variable '$CHECKOV_VERSION' More info: https://docs.docker.com/go/dockerfile/rule/undefined-var/
ARG HCLEDIT_VERSION=${HCLEDIT_VERSION:-false}

Check warning on line 37 in Dockerfile

View workflow job for this annotation

GitHub Actions / Ubuntu ARM

Variables should be defined before their use

UndefinedVar: Usage of undefined variable '$HCLEDIT_VERSION' More info: https://docs.docker.com/go/dockerfile/rule/undefined-var/

Check warning on line 37 in Dockerfile

View workflow job for this annotation

GitHub Actions / Ubuntu x64

Variables should be defined before their use

UndefinedVar: Usage of undefined variable '$HCLEDIT_VERSION' More info: https://docs.docker.com/go/dockerfile/rule/undefined-var/

Check warning on line 37 in Dockerfile

View workflow job for this annotation

GitHub Actions / Ubuntu x64

Variables should be defined before their use

UndefinedVar: Usage of undefined variable '$HCLEDIT_VERSION' More info: https://docs.docker.com/go/dockerfile/rule/undefined-var/
ARG INFRACOST_VERSION=${INFRACOST_VERSION:-false}

Check warning on line 38 in Dockerfile

View workflow job for this annotation

GitHub Actions / Ubuntu x64

Variables should be defined before their use

UndefinedVar: Usage of undefined variable '$INFRACOST_VERSION' More info: https://docs.docker.com/go/dockerfile/rule/undefined-var/

Check warning on line 38 in Dockerfile

View workflow job for this annotation

GitHub Actions / Ubuntu x64

Variables should be defined before their use

UndefinedVar: Usage of undefined variable '$INFRACOST_VERSION' More info: https://docs.docker.com/go/dockerfile/rule/undefined-var/
ARG TERRAFORM_DOCS_VERSION=${TERRAFORM_DOCS_VERSION:-false}

Check warning on line 39 in Dockerfile

View workflow job for this annotation

GitHub Actions / Ubuntu ARM

Variables should be defined before their use

UndefinedVar: Usage of undefined variable '$TERRAFORM_DOCS_VERSION' More info: https://docs.docker.com/go/dockerfile/rule/undefined-var/

Check warning on line 39 in Dockerfile

View workflow job for this annotation

GitHub Actions / Ubuntu x64

Variables should be defined before their use

UndefinedVar: Usage of undefined variable '$TERRAFORM_DOCS_VERSION' More info: https://docs.docker.com/go/dockerfile/rule/undefined-var/
ARG TERRAGRUNT_VERSION=${TERRAGRUNT_VERSION:-false}

Check warning on line 40 in Dockerfile

View workflow job for this annotation

GitHub Actions / Ubuntu ARM

Variables should be defined before their use

UndefinedVar: Usage of undefined variable '$TERRAGRUNT_VERSION' More info: https://docs.docker.com/go/dockerfile/rule/undefined-var/

Check warning on line 40 in Dockerfile

View workflow job for this annotation

GitHub Actions / Ubuntu x64

Variables should be defined before their use

UndefinedVar: Usage of undefined variable '$TERRAGRUNT_VERSION' More info: https://docs.docker.com/go/dockerfile/rule/undefined-var/

Check warning on line 40 in Dockerfile

View workflow job for this annotation

GitHub Actions / Ubuntu x64

Variables should be defined before their use

UndefinedVar: Usage of undefined variable '$TERRAGRUNT_VERSION' More info: https://docs.docker.com/go/dockerfile/rule/undefined-var/
ARG TERRASCAN_VERSION=${TERRASCAN_VERSION:-false}

Check warning on line 41 in Dockerfile

View workflow job for this annotation

GitHub Actions / Ubuntu x64

Variables should be defined before their use

UndefinedVar: Usage of undefined variable '$TERRASCAN_VERSION' More info: https://docs.docker.com/go/dockerfile/rule/undefined-var/

Check warning on line 41 in Dockerfile

View workflow job for this annotation

GitHub Actions / Ubuntu x64

Variables should be defined before their use

UndefinedVar: Usage of undefined variable '$TERRASCAN_VERSION' More info: https://docs.docker.com/go/dockerfile/rule/undefined-var/
ARG TFLINT_VERSION=${TFLINT_VERSION:-false}

Check warning on line 42 in Dockerfile

View workflow job for this annotation

GitHub Actions / Ubuntu ARM

Variables should be defined before their use

UndefinedVar: Usage of undefined variable '$TFLINT_VERSION' More info: https://docs.docker.com/go/dockerfile/rule/undefined-var/

Check warning on line 42 in Dockerfile

View workflow job for this annotation

GitHub Actions / Ubuntu x64

Variables should be defined before their use

UndefinedVar: Usage of undefined variable '$TFLINT_VERSION' More info: https://docs.docker.com/go/dockerfile/rule/undefined-var/
ARG TFSEC_VERSION=${TFSEC_VERSION:-false}
ARG TFUPDATE_VERSION=${TFUPDATE_VERSION:-false}
ARG TRIVY_VERSION=${TRIVY_VERSION:-false}

Check warning on line 45 in Dockerfile

View workflow job for this annotation

GitHub Actions / Ubuntu ARM

Variables should be defined before their use

UndefinedVar: Usage of undefined variable '$TRIVY_VERSION' More info: https://docs.docker.com/go/dockerfile/rule/undefined-var/

Check warning on line 45 in Dockerfile

View workflow job for this annotation

GitHub Actions / Ubuntu x64

Variables should be defined before their use

UndefinedVar: Usage of undefined variable '$TRIVY_VERSION' More info: https://docs.docker.com/go/dockerfile/rule/undefined-var/

Check warning on line 45 in Dockerfile

View workflow job for this annotation

GitHub Actions / Ubuntu x64

Variables should be defined before their use

UndefinedVar: Usage of undefined variable '$TRIVY_VERSION' More info: https://docs.docker.com/go/dockerfile/rule/undefined-var/


# Tricky thing to install all tools by set only one arg.
Expand All @@ -65,7 +65,9 @@
echo "TRIVY_VERSION=latest" >> /.env \
; fi

# DL3064 - empty default; real value passed via --build-arg, never hardcoded
# hadolint ignore=DL3064
ARG GITHUB_TOKEN=${GITHUB_TOKEN:-""}

Check warning on line 70 in Dockerfile

View workflow job for this annotation

GitHub Actions / Ubuntu ARM

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ARG "GITHUB_TOKEN") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/

Check warning on line 70 in Dockerfile

View workflow job for this annotation

GitHub Actions / Ubuntu x64

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ARG "GITHUB_TOKEN") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/

Check warning on line 70 in Dockerfile

View workflow job for this annotation

GitHub Actions / Ubuntu x64

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ARG "GITHUB_TOKEN") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/

# Docker `RUN`s shouldn't be consolidated here
# hadolint global ignore=DL3059
Expand Down Expand Up @@ -116,6 +118,8 @@
git=~2 \
# All hooks deps
bash=~5 \
# needed by tools/install/<tool>.sh for --tool-version downloads
curl=~8 \
# pre-commit-hooks deps: https://github.com/pre-commit/pre-commit-hooks
musl-dev=~1 \
gcc=~14 \
Expand Down Expand Up @@ -150,9 +154,11 @@

COPY tools/entrypoint.sh /entrypoint.sh

ENV PRE_COMMIT_COLOR=${PRE_COMMIT_COLOR:-always}

Check warning on line 157 in Dockerfile

View workflow job for this annotation

GitHub Actions / Ubuntu ARM

Variables should be defined before their use

UndefinedVar: Usage of undefined variable '$PRE_COMMIT_COLOR' More info: https://docs.docker.com/go/dockerfile/rule/undefined-var/

Check warning on line 157 in Dockerfile

View workflow job for this annotation

GitHub Actions / Ubuntu x64

Variables should be defined before their use

UndefinedVar: Usage of undefined variable '$PRE_COMMIT_COLOR' More info: https://docs.docker.com/go/dockerfile/rule/undefined-var/

# DL3064 - empty default; real value passed via `-e` at runtime, never hardcoded
# hadolint ignore=DL3064
ENV INFRACOST_API_KEY=${INFRACOST_API_KEY:-}

Check warning on line 161 in Dockerfile

View workflow job for this annotation

GitHub Actions / Ubuntu x64

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ENV "INFRACOST_API_KEY") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/

Check warning on line 161 in Dockerfile

View workflow job for this annotation

GitHub Actions / Ubuntu x64

Variables should be defined before their use

UndefinedVar: Usage of undefined variable '$INFRACOST_API_KEY' More info: https://docs.docker.com/go/dockerfile/rule/undefined-var/
ENV INFRACOST_SKIP_UPDATE_CHECK=${INFRACOST_SKIP_UPDATE_CHECK:-false}

ENTRYPOINT [ "/entrypoint.sh" ]
106 changes: 106 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ If you want to support the development of `pre-commit-terraform` and [many other
* [All hooks: Set env vars inside hook at runtime](#all-hooks-set-env-vars-inside-hook-at-runtime)
* [All hooks: Disable color output](#all-hooks-disable-color-output)
* [All hooks: Log levels](#all-hooks-log-levels)
* [Most hooks: Pin a specific tool version](#most-hooks-pin-a-specific-tool-version)
* [Keeping pinned versions up-to-date using Renovate](#keeping-pinned-versions-up-to-date-using-renovate)
* [Many hooks: Parallelism](#many-hooks-parallelism)
* [checkov (deprecated) and terraform\_checkov](#checkov-deprecated-and-terraform_checkov)
* [infracost\_breakdown](#infracost_breakdown)
Expand All @@ -77,6 +79,7 @@ If you want to support the development of `pre-commit-terraform` and [many other
* [About Docker image security](#about-docker-image-security)
* [File Permissions](#file-permissions)
* [Download Terraform modules from private GitHub repositories](#download-terraform-modules-from-private-github-repositories)
* [Mount tools cache directory](#mount-tools-cache-directory)
* [GitHub Actions](#github-actions)
* [Authors](#authors)
* [License](#license)
Expand Down Expand Up @@ -434,6 +437,89 @@ PCT_LOG=trace pre-commit run -a

Less verbose log levels will be implemented in [#562](https://github.com/antonbabenko/pre-commit-terraform/issues/562).

### Most hooks: Pin a specific tool version

> All hooks, which wrap a tool distributed as a downloadable release asset. Not supported for `checkov`/`terraform_checkov` (distributed via PyPi) and for deprecated `terraform_docs_replace` hook.

1. You can pin a specific version of the wrapped tool per hook, independent of whatever is on your `$PATH` or baked into the Docker image. If that version isn't already cached locally, it's downloaded from the tool's GitHub releases on first use, then reused (without re-downloading) on every subsequent run.

@coderabbitai coderabbitai Bot Jul 28, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Avoid claiming every binary is downloaded from GitHub releases.

Terraform/OpenTofu resolution uses HashiCorp release installers, so this should say “the tool’s upstream release assets” (or similar) rather than specifically “GitHub releases.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` at line 443, Update the version-pinning description near the
wrapped-tool download behavior to replace the GitHub-specific wording with “the
tool’s upstream release assets” or equivalent, while preserving the existing
caching and first-use download behavior.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see an issue here. "Github release" is more undersandable, IMO.
@yermulnik ?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"GitHub releases" describes the feature, but e.g. "GitHub releases page" (or with capitalized "Releases" to mark the section on the web-site) describes the actual location where we download assets from.
So I'd add "page" after "releases" to provide clarity. However, tech-savvies do indeed often say "Github releases" meaning "Github Releases page" =)
Up to you 👍

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!


Config example:

```yaml
- id: terraform_tflint
args:
- --hook-config=--tool-version=0.50.0
Comment thread
Copilot marked this conversation as resolved.
```

2. The same `--tool-version` key also works for `terraform_validate`, `terraform_fmt` and `terraform_providers_lock`, which resolve their Terraform/OpenTofu binary through [`--tf-path`](#11-custom-terraform-binaries-and-opentofu-support) - by default it downloads/uses whichever of `terraform`/`opentofu` binary the rest of that precedence chain would otherwise have picked (`terraform`, unless it's missing from `$PATH` while `tofu` is present, in which case `opentofu`). To pick explicitly instead of relying on the auto-detection mechanism, set `--tf-path` to the literal value of `terraform`, `opentofu` or `tofu`:

```yaml
- id: terraform_validate
args:
- --hook-config=--tf-path=opentofu
- --hook-config=--tool-version=1.12.0
```
Comment thread
coderabbitai[bot] marked this conversation as resolved.

`--tf-path` and `--tool-version` either can be set on its own, or combined:

| `--tf-path` | `--tool-version` | Behavior |
| ------------------------------------------ | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| set to a literal path/binary name | unset | Use that local binary as-is (normal [`--tf-path`](#11-custom-terraform-binaries-and-opentofu-support) behavior); `--tool-version` is ignored |
| unset | set | Auto-detect `terraform`/`opentofu` as described above, then download/use that tool at the pinned version |
| set to `terraform`, `opentofu`, or `tofu` | set | Download/use the pinned version of whichever of the two was selected |
| set to anything else (e.g. an actual path) | set | Error - combined with `--tool-version`, `--tf-path` only accepts `terraform`, `opentofu`, or `tofu` |
| unset | unset | Falls back to the normal [`--tf-path`](#11-custom-terraform-binaries-and-opentofu-support) precedence chain (env vars, then `$PATH`) |

> [!TIP]
> 3. Since this resolves to a version-specific cached binary rather than mutating `$PATH`, the same hook can be listed multiple times with different pinned versions, e.g. to test compatibility across tool versions in one run:
>
> ```yaml
> - id: terraform_tflint
> args:
> - --hook-config=--tool-version=0.50.0
> - id: terraform_tflint
> args:
> - --hook-config=--tool-version=0.55.0
> ```

4. By default, if a different version of the tool is already on `$PATH`, the pinned version still wins (with a warning message logged) based on `--hook-config=--tool-version-mode=strict`. Set it to `prefer-local` (as opposite to `strict`) to invert that: if the tool already resolves via `$PATH`, this local binary is used as-is and no download is attempted; the pinned version is only downloaded/used as a fallback when nothing is found locally.

```yaml
- id: terraform_tflint
args:
- --hook-config=--tool-version=0.50.0
- --hook-config=--tool-version-mode=prefer-local
```

5. By default, downloaded binaries are cached under `$XDG_CACHE_HOME/pre-commit-terraform/` (or `$HOME/.cache/pre-commit-terraform/` if `XDG_CACHE_HOME` is unset). Override this location with the `PCT_TOOL_CACHE_DIR` environment variable - see [Mount tools cache directory](#mount-tools-cache-directory) for the Docker case.

6. If a `GITHUB_TOKEN` environment variable is set, it's inherited automatically to authenticate GitHub API requests made during version resolution, the same way it already is utilized for [building your own Docker image](#docker-usage).

#### Keeping pinned versions up-to-date using Renovate

Neither Renovate's built-in [`pre-commit` manager](https://docs.renovatebot.com/modules/manager/pre-commit/) (which only understands `repo:`/`rev:` and `additional_dependencies` for Go/Node/Python) nor its `dockerfileVersions` preset can "look" inside hook's `args:`, so the `--tool-version` pin needs its own [`customManagers`](https://docs.renovatebot.com/modules/manager/regex/) entry in your own `renovate.json5` config file, using the same `# renovate: datasource=... depName=...` annotation convention commonly used for Dockerfile `ARG *_VERSION` pins:

```yaml
- id: terraform_tflint
args:
# renovate: datasource=github-releases depName=terraform-linters/tflint
- --hook-config=--tool-version=0.50.0
```

```json5
{
customManagers: [
{
customType: "regex",
managerFilePatterns: ["/\\.pre-commit-config\\.ya?ml$/"],
matchStrings: [
"# renovate: datasource=(?<datasource>\\S+) depName=(?<depName>\\S+)\\s+-\\s+--hook-config=--tool-version=(?<currentValue>\\S+)",
],
},
],
}
```

### Many hooks: Parallelism

> All, except deprecated hooks: `checkov`, `terraform_docs_replace` and hooks which can't be paralleled this way: `infracost_breakdown`, `terraform_wrapper_module_for_each`.
Expand Down Expand Up @@ -1295,6 +1381,26 @@ Finally, you can execute `docker run` with an additional volume mount so that th
docker run --rm -e "USERID=$(id -u):$(id -g)" -v ~/.netrc:/root/.netrc -v $(pwd):/lint -w /lint ghcr.io/antonbabenko/pre-commit-terraform:latest run -a
```

### Mount tools cache directory

A container's own filesystem is discarded after `docker run` exits, so a version downloaded via [`--tool-version`](#most-hooks-pin-a-specific-tool-version) would otherwise be re-downloaded on every single run. Mount the cache directory as a volume to persist it across runs, the same way you would for [`TF_PLUGIN_CACHE_DIR`](https://developer.hashicorp.com/terraform/cli/config/config-file#provider-plugin-cache):

> [!IMPORTANT]
> With a non-root `USERID` (the [recommended](#4-run) way to run the image), do not mount the cache under `/root/...`: the container switches to that UID/GID via `su-exec`, which neither grants it permission to traverse `/root` nor changes `$HOME`, so the mount would be unreachable and pinned-tool resolution would fail instead of using it. Point `PCT_TOOL_CACHE_DIR` at a container path any UID can write to instead (e.g. under `/tmp`), and pre-create the host directory so Docker doesn't auto-create it as `root`-owned on first mount.

```bash
TAG=latest
mkdir -p ~/.cache/pre-commit-terraform
docker run \
-e "USERID=$(id -u):$(id -g)" \
-e PCT_TOOL_CACHE_DIR=/tmp/pre-commit-terraform-cache \
-v ~/.cache/pre-commit-terraform:/tmp/pre-commit-terraform-cache \
-v $(pwd):/lint -w /lint \
ghcr.io/antonbabenko/pre-commit-terraform:$TAG run -a
```

If you set `PCT_TOOL_CACHE_DIR` to a different custom location, mount the host cache directory at that same container path instead.

## GitHub Actions

You can use this hook in your GitHub Actions workflow together with [pre-commit](https://pre-commit.com). To easy up
Expand Down
3 changes: 3 additions & 0 deletions hatch.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
include = [
'.codecov.yml',
'.coveragerc',
'hooks/',
'lib_getopt',
'src/',
'tests/',
'tools/install/',
'pytest.ini',
'tox.ini',
]
Expand Down
Loading
Loading