diff --git a/README.md b/README.md index 59f01426e..f09ad21b2 100644 --- a/README.md +++ b/README.md @@ -970,7 +970,15 @@ To replicate functionality in `terraform_docs` hook: ### terraform_validate > [!IMPORTANT] -> If you use [`TF_PLUGIN_CACHE_DIR`](https://developer.hashicorp.com/terraform/cli/config/config-file#provider-plugin-cache), we recommend enabling `--hook-config=--retry-once-with-cleanup=true` or disabling parallelism (`--hook-config=--parallelism-limit=1`) to avoid [race conditions when `terraform init` writes to it](https://github.com/hashicorp/terraform/issues/31964). +> If you use both: +> +> 1. Global Provider Cache. [Terraform docs](https://developer.hashicorp.com/terraform/cli/config/config-file#provider-plugin-cache), [OpenTofu docs](https://opentofu.org/docs/cli/config/config-file/#provider-plugin-cache) +> 2. `tofu` < v1.10 or any `terraform` version +> +> We recommend to do one of: +> +> * Disable parallelism (`--hook-config=--parallelism-limit=1`) as it can cause [race conditions when `terraform init` writes to it](https://github.com/hashicorp/terraform/issues/31964). +> * Install [OpenTofu v1.10+](https://opentofu.org/blog/help-us-test-opentofu-1-10-0-alpha2/#global-provider-cache-locking) even if you use Terraform - it will be used just for `t init` operations. 1. `terraform_validate` supports custom arguments so you can pass supported `-no-color` or `-json` flags: diff --git a/hooks/_common.sh b/hooks/_common.sh index fd26dfce1..c2bfce1a1 100644 --- a/hooks/_common.sh +++ b/hooks/_common.sh @@ -555,10 +555,20 @@ function common::terraform_init { init_output=$("$tf_path" init -backend=false "${TF_INIT_ARGS[@]}" 2>&1) exit_code=$? else + # The global provider cache is safe for concurrent use by multiple processes for OpenTofu v1.10+ + # More details - https://github.com/opentofu/opentofu/pull/1878 + # For that reason, we switch to `tofu init` when possible + local tf_init_path=$tf_path + if [[ "$($tf_path --version | head -1 | grep -o '^Terraform')" == "Terraform" ]] && + common::tofu_version_ge_1.10; then + tf_init_path=$(command -v tofu) + common::colorify "green" "Using OpenTofu binary ($tf_init_path) for Terraform init operations, as it supports concurrent provider initialization." + fi + # Locking just doesn't work, and the below works quicker instead. Details: # https://github.com/hashicorp/terraform/issues/31964#issuecomment-1939869453 for i in {1..10}; do - init_output=$("$tf_path" init -backend=false "${TF_INIT_ARGS[@]}" 2>&1) + init_output=$("$tf_init_path" init -backend=false "${TF_INIT_ARGS[@]}" 2>&1) exit_code=$? if [ $exit_code -eq 0 ]; then @@ -609,6 +619,36 @@ function common::export_provided_env_vars { done } +####################################################################### +# Check if the installed Tofu version is >=1.10.0 or not +# +# This function determines whether the tofu version allows using +# parallelism. +# +# Returns: +# - 0 if version >= 1.10.0 +# - 1 if version < 1.10.0 +# Defaults to 1 if version cannot be determined +####################################################################### +# TODO: Drop after Jun 2027. Two years to upgrade is more than enough. +function common::tofu_version_ge_1.10 { + local tofu_version + + # Extract version number (e.g., "tofu version v1.10.4" -> "1.10") + tofu_version=$(tofu --version 2> /dev/null | grep -oE '[0-9]+\.[0-9]+') + # If we can't parse version, default to older command + [[ ! $tofu_version ]] && return 1 + + local major minor + IFS='.' read -r major minor <<< "$tofu_version" + # New functionality was added in v1.10.0 (June 2025) + if [[ $major -gt 1 || ($major -eq 1 && $minor -ge 10) ]]; then + return 0 + fi + + return 1 +} + ####################################################################### # Check if the installed Terragrunt version is >=0.78.0 or not #