Skip to content

Commit c7011c0

Browse files
den-isyermulnikMaxymVlasov
authored
feat: Support set custom TF/OpenTofu binary. | If you use a custom Docker image build, please note that TERRAFORM_VERSION now must be provided (antonbabenko#670)
--------- Co-authored-by: George L. Yermulnik <[email protected]> Co-authored-by: Maksym Vlasov <[email protected]>
1 parent 1ac4f2d commit c7011c0

18 files changed

+189
-51
lines changed

.github/.container-structure-test-config.yaml

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,6 @@ commandTests:
1010
args: ["-V"]
1111
expectedOutput: ["^pre-commit ([0-9]+\\.){2}[0-9]+\\n$"]
1212

13-
- name: "terraform"
14-
command: "terraform"
15-
args: ["-version"]
16-
expectedOutput: ["^Terraform v([0-9]+\\.){2}[0-9]+\\n"]
17-
1813
- name: "gcc"
1914
command: "gcc"
2015
args: ["--version"]
@@ -30,6 +25,16 @@ commandTests:
3025
args: ["--version"]
3126
expectedOutput: ["^Infracost v([0-9]+\\.){2}[0-9]+"]
3227

28+
- name: "opentofu"
29+
command: "tofu"
30+
args: ["-version"]
31+
expectedOutput: ["^OpenTofu v([0-9]+\\.){2}[0-9]+\\n"]
32+
33+
- name: "terraform"
34+
command: "terraform"
35+
args: ["-version"]
36+
expectedOutput: ["^Terraform v([0-9]+\\.){2}[0-9]+\\n"]
37+
3338
- name: "terraform-docs"
3439
command: "terraform-docs"
3540
args: ["--version"]

.github/ISSUE_TEMPLATE/bug_report_local_install.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ Linux DESKTOP-C7315EF 5.4.72-microsoft-standard-WSL2 #1 SMP Wed Oct 28 23:40:43
7878
bash << EOF
7979
bash --version | head -n 1 2>/dev/null || echo "bash SKIPPED"
8080
pre-commit --version 2>/dev/null || echo "pre-commit SKIPPED"
81+
tofu --version | head -n 1 2>/dev/null || echo "opentofu SKIPPED"
8182
terraform --version | head -n 1 2>/dev/null || echo "terraform SKIPPED"
8283
python --version 2>/dev/null || echo "python SKIPPED"
8384
python3 --version 2>/dev/null || echo "python3 SKIPPED"

Dockerfile

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,18 @@ COPY tools/install/ /install/
2020
# Install required tools
2121
#
2222
ARG PRE_COMMIT_VERSION=${PRE_COMMIT_VERSION:-latest}
23-
ARG TERRAFORM_VERSION=${TERRAFORM_VERSION:-latest}
24-
2523
RUN touch /.env && \
26-
if [ "$PRE_COMMIT_VERSION" = "false" ] || [ "$TERRAFORM_VERSION" = "false" ]; then \
24+
if [ "$PRE_COMMIT_VERSION" = "false" ]; then \
2725
echo "Vital software can't be skipped" && exit 1; \
2826
fi
29-
30-
3127
RUN /install/pre-commit.sh
32-
RUN /install/terraform.sh
3328

3429
#
3530
# Install tools
3631
#
32+
ARG OPENTOFU_VERSION=${OPENTOFU_VERSION:-false}
33+
ARG TERRAFORM_VERSION=${TERRAFORM_VERSION:-false}
34+
3735
ARG CHECKOV_VERSION=${CHECKOV_VERSION:-false}
3836
ARG HCLEDIT_VERSION=${HCLEDIT_VERSION:-false}
3937
ARG INFRACOST_VERSION=${INFRACOST_VERSION:-false}
@@ -51,6 +49,9 @@ ARG TRIVY_VERSION=${TRIVY_VERSION:-false}
5149
# specified in step below
5250
ARG INSTALL_ALL=${INSTALL_ALL:-false}
5351
RUN if [ "$INSTALL_ALL" != "false" ]; then \
52+
echo "OPENTOFU_VERSION=latest" >> /.env && \
53+
echo "TERRAFORM_VERSION=latest" >> /.env && \
54+
\
5455
echo "CHECKOV_VERSION=latest" >> /.env && \
5556
echo "HCLEDIT_VERSION=latest" >> /.env && \
5657
echo "INFRACOST_VERSION=latest" >> /.env && \
@@ -63,6 +64,9 @@ RUN if [ "$INSTALL_ALL" != "false" ]; then \
6364
echo "TRIVY_VERSION=latest" >> /.env \
6465
; fi
6566

67+
RUN /install/opentofu.sh
68+
RUN /install/terraform.sh
69+
6670
RUN /install/checkov.sh
6771
RUN /install/hcledit.sh
6872
RUN /install/infracost.sh
@@ -79,7 +83,9 @@ RUN /install/trivy.sh
7983
RUN . /.env && \
8084
F=tools_versions_info && \
8185
pre-commit --version >> $F && \
82-
./terraform --version | head -n 1 >> $F && \
86+
(if [ "$OPENTOFU_VERSION" != "false" ]; then echo "./tofu --version | head -n 1" >> $F; else echo "opentofu SKIPPED" >> $F ; fi) && \
87+
(if [ "$TERRAFORM_VERSION" != "false" ]; then echo "./terraform --version | head -n 1" >> $F; else echo "terraform SKIPPED" >> $F ; fi) && \
88+
\
8389
(if [ "$CHECKOV_VERSION" != "false" ]; then echo "checkov $(checkov --version)" >> $F; else echo "checkov SKIPPED" >> $F ; fi) && \
8490
(if [ "$HCLEDIT_VERSION" != "false" ]; then echo "hcledit $(./hcledit version)" >> $F; else echo "hcledit SKIPPED" >> $F ; fi) && \
8591
(if [ "$INFRACOST_VERSION" != "false" ]; then echo "$(./infracost --version)" >> $F; else echo "infracost SKIPPED" >> $F ; fi) && \

README.md

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ If you are using `pre-commit-terraform` already or want to support its developme
2929
* [Table of content](#table-of-content)
3030
* [How to install](#how-to-install)
3131
* [1. Install dependencies](#1-install-dependencies)
32+
* [1.1 Custom Terraform binaries and OpenTofu support](#11-custom-terraform-binaries-and-opentofu-support)
3233
* [2. Install the pre-commit hook globally](#2-install-the-pre-commit-hook-globally)
3334
* [3. Add configs and hooks](#3-add-configs-and-hooks)
3435
* [4. Run](#4-run)
@@ -67,7 +68,7 @@ If you are using `pre-commit-terraform` already or want to support its developme
6768
### 1. Install dependencies
6869

6970
* [`pre-commit`](https://pre-commit.com/#install),
70-
<sub><sup>[`terraform`](https://www.terraform.io/downloads.html),
71+
<sub><sup>[`terraform`](https://www.terraform.io/downloads.html) or [`opentofu`](https://opentofu.org/docs/intro/install/),
7172
<sub><sup>[`git`](https://git-scm.com/downloads),
7273
<sub><sup>[BASH `3.2.57` or newer](https://www.gnu.org/software/bash/#download),
7374
<sub><sup>Internet connection (on first run),
@@ -77,17 +78,31 @@ If you are using `pre-commit-terraform` already or want to support its developme
7778
<sub><sup>Some basic physical laws,
7879
<sub><sup>Hope that it all will work.
7980
</sup></sub></sup></sub></sup></sub></sup></sub></sup></sub></sup></sub></sup></sub></sup></sub></sup></sub><br><br>
80-
* [`checkov`](https://github.com/bridgecrewio/checkov) required for `terraform_checkov` hook.
81-
* [`terraform-docs`](https://github.com/terraform-docs/terraform-docs) required for `terraform_docs` hook.
82-
* [`terragrunt`](https://terragrunt.gruntwork.io/docs/getting-started/install/) required for `terragrunt_validate` hook.
83-
* [`terrascan`](https://github.com/tenable/terrascan) required for `terrascan` hook.
84-
* [`TFLint`](https://github.com/terraform-linters/tflint) required for `terraform_tflint` hook.
85-
* [`TFSec`](https://github.com/liamg/tfsec) required for `terraform_tfsec` hook.
86-
* [`Trivy`](https://github.com/aquasecurity/trivy) required for `terraform_trivy` hook.
87-
* [`infracost`](https://github.com/infracost/infracost) required for `infracost_breakdown` hook.
88-
* [`jq`](https://github.com/stedolan/jq) required for `terraform_validate` with `--retry-once-with-cleanup` flag, and for `infracost_breakdown` hook.
89-
* [`tfupdate`](https://github.com/minamijoyo/tfupdate) required for `tfupdate` hook.
90-
* [`hcledit`](https://github.com/minamijoyo/hcledit) required for `terraform_wrapper_module_for_each` hook.
81+
* [`checkov`](https://github.com/bridgecrewio/checkov) required for `terraform_checkov` hook
82+
* [`terraform-docs`](https://github.com/terraform-docs/terraform-docs) required for `terraform_docs` hook
83+
* [`terragrunt`](https://terragrunt.gruntwork.io/docs/getting-started/install/) required for `terragrunt_validate` hook
84+
* [`terrascan`](https://github.com/tenable/terrascan) required for `terrascan` hook
85+
* [`TFLint`](https://github.com/terraform-linters/tflint) required for `terraform_tflint` hook
86+
* [`TFSec`](https://github.com/liamg/tfsec) required for `terraform_tfsec` hook
87+
* [`Trivy`](https://github.com/aquasecurity/trivy) required for `terraform_trivy` hook
88+
* [`infracost`](https://github.com/infracost/infracost) required for `infracost_breakdown` hook
89+
* [`jq`](https://github.com/stedolan/jq) required for `terraform_validate` with `--retry-once-with-cleanup` flag, and for `infracost_breakdown` hook
90+
* [`tfupdate`](https://github.com/minamijoyo/tfupdate) required for `tfupdate` hook
91+
* [`hcledit`](https://github.com/minamijoyo/hcledit) required for `terraform_wrapper_module_for_each` hook
92+
93+
94+
#### 1.1 Custom Terraform binaries and OpenTofu support
95+
96+
It is possible to set custom path to `terraform` binary.
97+
This makes it possible to use [OpenTofu](https://opentofu.org) binary `tofu` instead of `terraform`.
98+
99+
How binary discovery works and how you can redefine it (first matched takes precedence):
100+
101+
1. Check if per hook configuration `--hook-config=--tf-path=<path_to_binary_or_binary_name>` is set
102+
2. Check if `PCT_TFPATH=<path_to_binary_or_binary_name>` environment variable is set
103+
3. Check if `TERRAGRUNT_TFPATH=<path_to_binary_or_binary_name>` environment variable is set
104+
4. Check if `terraform` binary can be found in the user's $PATH
105+
5. Check if `tofu` binary can be found in the user's $PATH
91106

92107
<details><summary><b>Docker</b></summary><br>
93108

@@ -120,17 +135,18 @@ To install a specific version of individual tools, define it using `--build-arg`
120135
```bash
121136
docker build -t pre-commit-terraform \
122137
--build-arg PRE_COMMIT_VERSION=latest \
123-
--build-arg TERRAFORM_VERSION=latest \
138+
--build-arg OPENTOFU_VERSION=latest \
139+
--build-arg TERRAFORM_VERSION=1.5.7 \
124140
--build-arg CHECKOV_VERSION=2.0.405 \
141+
--build-arg HCLEDIT_VERSION=latest \
125142
--build-arg INFRACOST_VERSION=latest \
126143
--build-arg TERRAFORM_DOCS_VERSION=0.15.0 \
127144
--build-arg TERRAGRUNT_VERSION=latest \
128145
--build-arg TERRASCAN_VERSION=1.10.0 \
129146
--build-arg TFLINT_VERSION=0.31.0 \
130147
--build-arg TFSEC_VERSION=latest \
131-
--build-arg TRIVY_VERSION=latest \
132148
--build-arg TFUPDATE_VERSION=latest \
133-
--build-arg HCLEDIT_VERSION=latest \
149+
--build-arg TRIVY_VERSION=latest \
134150
.
135151
```
136152

hooks/_common.sh

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,8 @@ function common::per_dir_hook {
283283
# despite there's only one positional ARG left
284284
local -a -r files=("$@")
285285

286+
local -r tf_path=$(common::get_tf_binary_path)
287+
286288
# check is (optional) function defined
287289
if [ "$(type -t run_hook_on_whole_repo)" == function ] &&
288290
# check is hook run via `pre-commit run --all`
@@ -383,7 +385,7 @@ function common::per_dir_hook {
383385
pushd "$dir_path" > /dev/null
384386
fi
385387

386-
per_dir_hook_unique_part "$dir_path" "$change_dir_in_unique_part" "$parallelism_disabled" "${args[@]}"
388+
per_dir_hook_unique_part "$dir_path" "$change_dir_in_unique_part" "$parallelism_disabled" "$tf_path" "${args[@]}"
387389
} &
388390
pids+=("$!")
389391

@@ -445,13 +447,66 @@ function common::colorify {
445447
echo -e "${COLOR}${TEXT}${RESET}" >&2
446448
}
447449

450+
#######################################################################
451+
# Get Terraform/OpenTofu binary path
452+
# Allows user to set the path to custom Terraform or OpenTofu binary
453+
# Globals (init and populate):
454+
# HOOK_CONFIG (array) arguments that configure hook behavior
455+
# PCT_TFPATH (string) user defined env var with path to Terraform/OpenTofu binary
456+
# TERRAGRUNT_TFPATH (string) user defined env var with path to Terraform/OpenTofu binary
457+
# Outputs:
458+
# If failed - exit 1 with error message about missing Terraform/OpenTofu binary
459+
#######################################################################
460+
function common::get_tf_binary_path {
461+
local hook_config_tf_path
462+
463+
for config in "${HOOK_CONFIG[@]}"; do
464+
if [[ $config == --tf-path=* ]]; then
465+
hook_config_tf_path=${config#*=}
466+
hook_config_tf_path=${hook_config_tf_path%;}
467+
break
468+
fi
469+
done
470+
471+
# direct hook config, has the highest precedence
472+
if [[ $hook_config_tf_path ]]; then
473+
echo "$hook_config_tf_path"
474+
return
475+
476+
# environment variable
477+
elif [[ $PCT_TFPATH ]]; then
478+
echo "$PCT_TFPATH"
479+
return
480+
481+
# Maybe there is a similar setting for Terragrunt already
482+
elif [[ $TERRAGRUNT_TFPATH ]]; then
483+
echo "$TERRAGRUNT_TFPATH"
484+
return
485+
486+
# check if Terraform binary is available
487+
elif command -v terraform &> /dev/null; then
488+
command -v terraform
489+
return
490+
491+
# finally, check if Tofu binary is available
492+
elif command -v tofu &> /dev/null; then
493+
command -v tofu
494+
return
495+
496+
else
497+
common::colorify "red" "Neither Terraform nor OpenTofu binary could be found. Please either set the \"--tf-path\" hook configuration argument, or set the \"PCT_TFPATH\" environment variable, or set the \"TERRAGRUNT_TFPATH\" environment variable, or install Terraform or OpenTofu globally."
498+
exit 1
499+
fi
500+
}
501+
448502
#######################################################################
449503
# Run terraform init command
450504
# Arguments:
451505
# command_name (string) command that will tun after successful init
452506
# dir_path (string) PATH to dir relative to git repo root.
453507
# Can be used in error logging
454508
# parallelism_disabled (bool) if true - skip lock mechanism
509+
# tf_path (string) PATH to Terraform/OpenTofu binary
455510
# Globals (init and populate):
456511
# TF_INIT_ARGS (array) arguments for `terraform init` command
457512
# TF_PLUGIN_CACHE_DIR (string) user defined env var with name of the directory
@@ -464,6 +519,7 @@ function common::terraform_init {
464519
local -r command_name=$1
465520
local -r dir_path=$2
466521
local -r parallelism_disabled=$3
522+
local -r tf_path=$4
467523

468524
local exit_code=0
469525
local init_output
@@ -480,13 +536,13 @@ function common::terraform_init {
480536
# Plugin cache dir can't be written concurrently or read during write
481537
# https://github.com/hashicorp/terraform/issues/31964
482538
if [[ -z $TF_PLUGIN_CACHE_DIR || $parallelism_disabled == true ]]; then
483-
init_output=$(terraform init -backend=false "${TF_INIT_ARGS[@]}" 2>&1)
539+
init_output=$($tf_path init -backend=false "${TF_INIT_ARGS[@]}" 2>&1)
484540
exit_code=$?
485541
else
486542
# Locking just doesn't work, and the below works quicker instead. Details:
487543
# https://github.com/hashicorp/terraform/issues/31964#issuecomment-1939869453
488544
for i in {1..10}; do
489-
init_output=$(terraform init -backend=false "${TF_INIT_ARGS[@]}" 2>&1)
545+
init_output=$($tf_path init -backend=false "${TF_INIT_ARGS[@]}" 2>&1)
490546
exit_code=$?
491547

492548
if [ $exit_code -eq 0 ]; then

hooks/terraform_checkov.sh

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ function main {
3636
# Availability depends on hook.
3737
# parallelism_disabled (bool) if true - skip lock mechanism
3838
# args (array) arguments that configure wrapped tool behavior
39+
# tf_path (string) PATH to Terraform/OpenTofu binary
3940
# Outputs:
4041
# If failed - print out hook checks status
4142
#######################################################################
@@ -46,7 +47,9 @@ function per_dir_hook_unique_part {
4647
local -r change_dir_in_unique_part="$2"
4748
# shellcheck disable=SC2034 # Unused var.
4849
local -r parallelism_disabled="$3"
49-
shift 3
50+
# shellcheck disable=SC2034 # Unused var.
51+
local -r tf_path="$4"
52+
shift 4
5053
local -a -r args=("$@")
5154

5255
checkov -d . "${args[@]}"

hooks/terraform_fmt.sh

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ function main {
3333
# Availability depends on hook.
3434
# parallelism_disabled (bool) if true - skip lock mechanism
3535
# args (array) arguments that configure wrapped tool behavior
36+
# tf_path (string) PATH to Terraform/OpenTofu binary
3637
# Outputs:
3738
# If failed - print out hook checks status
3839
#######################################################################
@@ -43,11 +44,12 @@ function per_dir_hook_unique_part {
4344
local -r change_dir_in_unique_part="$2"
4445
# shellcheck disable=SC2034 # Unused var.
4546
local -r parallelism_disabled="$3"
46-
shift 3
47+
local -r tf_path="$4"
48+
shift 4
4749
local -a -r args=("$@")
4850

4951
# pass the arguments to hook
50-
terraform fmt "${args[@]}"
52+
$tf_path fmt "${args[@]}"
5153

5254
# return exit code to common::per_dir_hook
5355
local exit_code=$?

hooks/terraform_providers_lock.sh

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ function lockfile_contains_all_needed_sha {
8787
# Availability depends on hook.
8888
# parallelism_disabled (bool) if true - skip lock mechanism
8989
# args (array) arguments that configure wrapped tool behavior
90+
# tf_path (string) PATH to Terraform/OpenTofu binary
9091
# Outputs:
9192
# If failed - print out hook checks status
9293
#######################################################################
@@ -95,7 +96,8 @@ function per_dir_hook_unique_part {
9596
# shellcheck disable=SC2034 # Unused var.
9697
local -r change_dir_in_unique_part="$2"
9798
local -r parallelism_disabled="$3"
98-
shift 3
99+
local -r tf_path="$4"
100+
shift 4
99101
local -a -r args=("$@")
100102

101103
local platforms_count=0
@@ -138,7 +140,7 @@ function per_dir_hook_unique_part {
138140
common::colorify "yellow" "DEPRECATION NOTICE: We introduced '--mode' flag for this hook.
139141
Check migration instructions at https://github.com/antonbabenko/pre-commit-terraform#terraform_providers_lock
140142
"
141-
common::terraform_init 'terraform providers lock' "$dir_path" "$parallelism_disabled" || {
143+
common::terraform_init 'terraform providers lock' "$dir_path" "$parallelism_disabled" "$tf_path" || {
142144
exit_code=$?
143145
return $exit_code
144146
}
@@ -153,7 +155,7 @@ Check migration instructions at https://github.com/antonbabenko/pre-commit-terra
153155
#? Don't require `tf init` for providers, but required `tf init` for modules
154156
#? Mitigated by `function match_validate_errors` from terraform_validate hook
155157
# pass the arguments to hook
156-
terraform providers lock "${args[@]}"
158+
$tf_path providers lock "${args[@]}"
157159

158160
# return exit code to common::per_dir_hook
159161
exit_code=$?

hooks/terraform_tflint.sh

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ function main {
4646
# Availability depends on hook.
4747
# parallelism_disabled (bool) if true - skip lock mechanism
4848
# args (array) arguments that configure wrapped tool behavior
49+
# tf_path (string) PATH to Terraform/OpenTofu binary
4950
# Outputs:
5051
# If failed - print out hook checks status
5152
#######################################################################
@@ -54,7 +55,9 @@ function per_dir_hook_unique_part {
5455
local -r change_dir_in_unique_part="$2"
5556
# shellcheck disable=SC2034 # Unused var.
5657
local -r parallelism_disabled="$3"
57-
shift 3
58+
# shellcheck disable=SC2034 # Unused var.
59+
local -r tf_path="$4"
60+
shift 4
5861
local -a -r args=("$@")
5962

6063
if [ "$change_dir_in_unique_part" == "delegate_chdir" ]; then

hooks/terraform_tfsec.sh

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ function main {
3939
# Availability depends on hook.
4040
# parallelism_disabled (bool) if true - skip lock mechanism
4141
# args (array) arguments that configure wrapped tool behavior
42+
# tf_path (string) PATH to Terraform/OpenTofu binary
4243
# Outputs:
4344
# If failed - print out hook checks status
4445
#######################################################################
@@ -49,7 +50,9 @@ function per_dir_hook_unique_part {
4950
local -r change_dir_in_unique_part="$2"
5051
# shellcheck disable=SC2034 # Unused var.
5152
local -r parallelism_disabled="$3"
52-
shift 3
53+
# shellcheck disable=SC2034 # Unused var.
54+
local -r tf_path="$4"
55+
shift 4
5356
local -a -r args=("$@")
5457

5558
# pass the arguments to hook

0 commit comments

Comments
 (0)