Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(terraform_docs): Drop support for terraform-docs <0.12.0 #717

Merged
merged 3 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ If you want to support the development of `pre-commit-terraform` and [many other
<sub><sup>Hope that it all will work.
</sup></sub></sup></sub></sup></sub></sup></sub></sup></sub></sup></sub></sup></sub></sup></sub></sup></sub><br><br>
* [`checkov`](https://github.com/bridgecrewio/checkov) required for `terraform_checkov` hook
* [`terraform-docs`](https://github.com/terraform-docs/terraform-docs) required for `terraform_docs` hook
* [`terraform-docs`](https://github.com/terraform-docs/terraform-docs) 0.12.0+ required for `terraform_docs` hook
* [`terragrunt`](https://terragrunt.gruntwork.io/docs/getting-started/install/) required for `terragrunt_validate` and `terragrunt_valid_inputs` hooks
* [`terrascan`](https://github.com/tenable/terrascan) required for `terrascan` hook
* [`TFLint`](https://github.com/terraform-linters/tflint) required for `terraform_tflint` hook
Expand Down
300 changes: 17 additions & 283 deletions hooks/terraform_docs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ function main {
ARGS[i]=${ARGS[i]/--config=/--config=$(pwd)\/}
done
# shellcheck disable=SC2153 # False positive
terraform_docs_ "${HOOK_CONFIG[*]}" "${ARGS[*]}" "${FILES[@]}"
terraform_docs "${HOOK_CONFIG[*]}" "${ARGS[*]}" "${FILES[@]}"
}

#######################################################################
Expand All @@ -47,79 +47,25 @@ function replace_old_markers {
}

#######################################################################
# Function which prepares hacks for old versions of `terraform` and
# `terraform-docs` that them call `terraform_docs`
# Wrapper around `terraform-docs` tool that checks and changes/creates
# (depending on provided hook_config) terraform documentation in
# Markdown
# Arguments:
# hook_config (string with array) arguments that configure hook behavior
# args (string with array) arguments that configure wrapped tool behavior
# files (array) filenames to check
#######################################################################
function terraform_docs_ {
function terraform_docs {
local -r hook_config="$1"
local -r args="$2"
local args="$2"
shift 2
local -a -r files=("$@")

# Get hook settings
IFS=";" read -r -a configs <<< "$hook_config"

local hack_terraform_docs
local hack_terraform_docs=0
if [[ $(\command -V terraform 2> /dev/null) ]]; then
hack_terraform_docs=$(terraform version | sed -n 1p | grep -c 0.12) || true
fi

if [[ ! $(command -v terraform-docs) ]]; then
echo "ERROR: terraform-docs is required by terraform_docs pre-commit hook but is not installed or in the system's PATH."
exit 1
fi

local is_old_terraform_docs
is_old_terraform_docs=$(terraform-docs version | grep -o "v0.[1-7]\." | tail -1) || true

if [[ -z "$is_old_terraform_docs" ]]; then # Using terraform-docs 0.8+ (preferred)

terraform_docs "0" "${configs[*]}" "$args" "${files[@]}"

elif [[ "$hack_terraform_docs" == "1" ]]; then # Using awk script because terraform-docs is older than 0.8 and terraform 0.12 is used

if [[ ! $(command -v awk) ]]; then
echo "ERROR: awk is required for terraform-docs hack to work with Terraform 0.12."
exit 1
fi

local tmp_file_awk
tmp_file_awk=$(mktemp "${TMPDIR:-/tmp}/terraform-docs-XXXXXXXXXX")
terraform_docs_awk "$tmp_file_awk"
terraform_docs "$tmp_file_awk" "${configs[*]}" "$args" "${files[@]}"
rm -f "$tmp_file_awk"

else # Using terraform 0.11 and no awk script is needed for that

terraform_docs "0" "${configs[*]}" "$args" "${files[@]}"

fi
}

#######################################################################
# Wrapper around `terraform-docs` tool that check and change/create
# (depends on provided hook_config) terraform documentation in
# markdown format
# Arguments:
# terraform_docs_awk_file (string) filename where awk hack for old
# `terraform-docs` was written. Needed for TF 0.12+.
# Hack skipped when `terraform_docs_awk_file == "0"`
# hook_config (string with array) arguments that configure hook behavior
# args (string with array) arguments that configure wrapped tool behavior
# files (array) filenames to check
#######################################################################
function terraform_docs {
local -r terraform_docs_awk_file="$1"
local -r hook_config="$2"
local args="$3"
shift 3
local -a -r files=("$@")

local -a paths

local index=0
Expand All @@ -142,7 +88,7 @@ function terraform_docs {
local create_if_not_exist=false
local use_standard_markers=true

read -r -a configs <<< "$hook_config"
IFS=";" read -r -a configs <<< "$hook_config"

for c in "${configs[@]}"; do

Expand Down Expand Up @@ -260,64 +206,17 @@ function terraform_docs {

replace_old_markers "$output_file"

if [[ "$terraform_docs_awk_file" == "0" ]]; then
#? TF 0.12+ and terraform-docs 0.12.0+

#
# If `--add-to-existing-file=false` (default behavior), check if "hook markers" exist in file,
# and, if not, skip execution to avoid addition of terraform-docs section, as
# terraform-docs in 'inject' mode adds markers by default if they are not present
#
if [[ $add_to_existing == false ]]; then
have_marker=$(grep -o "$insertion_marker_begin" "$output_file") || unset have_marker
[[ ! $have_marker ]] && continue
fi
# shellcheck disable=SC2086
terraform-docs --output-mode="$output_mode" --output-file="$output_file" $tf_docs_formatter $args ./ > /dev/null

else
#? TF 0.12+ and terraform-docs < 0.8
#? Yes, we don't cover case of TF 0.12+ and terraform-docs 0.8-0.11
#? but I probably just drop this section in next release of the hook,
#? as there's no sense to support hacks for tool versions which were released more than 3 years ago

#
# If `--add-to-existing-file=true` set, check if "hook markers" exist in file,
# and, if not, append "hook markers" to the end of the file.
#
if [[ $add_to_existing == true ]]; then
have_marker=$(grep -o "$insertion_marker_begin" "$output_file") || unset have_marker

if [[ ! $have_marker ]]; then
# Use of insertion markers, when "add_to_existing=true" with no markers in the existing file
echo "$insertion_marker_begin" >> "$output_file"
echo "$insertion_marker_end" >> "$output_file"
fi
fi
# Can't append extension for mktemp, so renaming instead
local tmp_file_docs
tmp_file_docs=$(mktemp "${TMPDIR:-/tmp}/terraform-docs-XXXXXXXXXX")
mv "$tmp_file_docs" "$tmp_file_docs.tf"
local tmp_file_docs_tf
tmp_file_docs_tf="$tmp_file_docs.tf"

awk -f "$terraform_docs_awk_file" ./*.tf > "$tmp_file_docs_tf"

local -r tmp_file=$(mktemp)
# shellcheck disable=SC2086
terraform-docs --output-file="" $tf_docs_formatter $args "$tmp_file_docs_tf" > "$tmp_file"
rm -f "$tmp_file_docs_tf"

# Use of insertion markers to insert the terraform-docs output between the markers
# Replace content between markers with the placeholder - https://stackoverflow.com/questions/1212799/how-do-i-extract-lines-between-two-line-delimiters-in-perl#1212834
perl_expression="if (/$insertion_marker_begin/../$insertion_marker_end/) { print \$_ if /$insertion_marker_begin/; print \"I_WANT_TO_BE_REPLACED\\n\$_\" if /$insertion_marker_end/;} else { print \$_ }"
perl -i -ne "$perl_expression" "$output_file"

# Replace placeholder with the content of the file
perl -i -e 'open(F, "'"$tmp_file"'"); $f = join "", <F>; while(<>){if (/I_WANT_TO_BE_REPLACED/) {print $f} else {print $_};}' "$output_file"

rm -f "$tmp_file"
#
# If `--add-to-existing-file=false` (default behavior), check if "hook markers" exist in file,
# and, if not, skip execution to avoid addition of terraform-docs section, as
# terraform-docs in 'inject' mode adds markers by default if they are not present
#
if [[ $add_to_existing == false ]]; then
have_marker=$(grep -o "$insertion_marker_begin" "$output_file") || unset have_marker
[[ ! $have_marker ]] && continue
fi
# shellcheck disable=SC2086
terraform-docs --output-mode="$output_mode" --output-file="$output_file" $tf_docs_formatter $args ./ > /dev/null

popd > /dev/null
done
Expand All @@ -326,169 +225,4 @@ function terraform_docs {
rm -f "$config_file_no_color"
}

#######################################################################
# Function which creates file with `awk` hacks for old versions of
# `terraform-docs`
# Arguments:
# output_file (string) filename where hack will be written to
#######################################################################
function terraform_docs_awk {
local -r output_file=$1

cat << "EOF" > "$output_file"
# This script converts Terraform 0.12 variables/outputs to something suitable for `terraform-docs`
# As of terraform-docs v0.6.0, HCL2 is not supported. This script is a *dirty hack* to get around it.
# https://github.com/terraform-docs/terraform-docs/
# https://github.com/terraform-docs/terraform-docs/issues/62
# Script was originally found here: https://github.com/cloudposse/build-harness/blob/master/bin/terraform-docs.awk
{
if ( $0 ~ /\{/ ) {
braceCnt++
}
if ( $0 ~ /\}/ ) {
braceCnt--
}
# ----------------------------------------------------------------------------------------------
# variable|output "..." {
# ----------------------------------------------------------------------------------------------
# [END] variable/output block
if (blockCnt > 0 && blockTypeCnt == 0 && blockDefaultCnt == 0) {
if (braceCnt == 0 && blockCnt > 0) {
blockCnt--
print $0
}
}
# [START] variable or output block started
if ($0 ~ /^[[:space:]]*(variable|output)[[:space:]][[:space:]]*"(.*?)"/) {
# Normalize the braceCnt and block (should be 1 now)
braceCnt = 1
blockCnt = 1
# [CLOSE] "default" and "type" block
blockDefaultCnt = 0
blockTypeCnt = 0
# Print variable|output line
print $0
}
# ----------------------------------------------------------------------------------------------
# default = ...
# ----------------------------------------------------------------------------------------------
# [END] multiline "default" continues/ends
if (blockCnt > 0 && blockTypeCnt == 0 && blockDefaultCnt > 0) {
print $0
# Count opening blocks
blockDefaultCnt += gsub(/\(/, "")
blockDefaultCnt += gsub(/\[/, "")
blockDefaultCnt += gsub(/\{/, "")
# Count closing blocks
blockDefaultCnt -= gsub(/\)/, "")
blockDefaultCnt -= gsub(/\]/, "")
blockDefaultCnt -= gsub(/\}/, "")
}
# [START] multiline "default" statement started
if (blockCnt > 0 && blockTypeCnt == 0 && blockDefaultCnt == 0) {
if ($0 ~ /^[[:space:]][[:space:]]*(default)[[:space:]][[:space:]]*=/) {
if ($3 ~ "null") {
print " default = \"null\""
} else {
print $0
# Count opening blocks
blockDefaultCnt += gsub(/\(/, "")
blockDefaultCnt += gsub(/\[/, "")
blockDefaultCnt += gsub(/\{/, "")
# Count closing blocks
blockDefaultCnt -= gsub(/\)/, "")
blockDefaultCnt -= gsub(/\]/, "")
blockDefaultCnt -= gsub(/\}/, "")
}
}
}
# ----------------------------------------------------------------------------------------------
# type = ...
# ----------------------------------------------------------------------------------------------
# [END] multiline "type" continues/ends
if (blockCnt > 0 && blockTypeCnt > 0 && blockDefaultCnt == 0) {
# The following 'print $0' would print multiline type definitions
#print $0
# Count opening blocks
blockTypeCnt += gsub(/\(/, "")
blockTypeCnt += gsub(/\[/, "")
blockTypeCnt += gsub(/\{/, "")
# Count closing blocks
blockTypeCnt -= gsub(/\)/, "")
blockTypeCnt -= gsub(/\]/, "")
blockTypeCnt -= gsub(/\}/, "")
}
# [START] multiline "type" statement started
if (blockCnt > 0 && blockTypeCnt == 0 && blockDefaultCnt == 0) {
if ($0 ~ /^[[:space:]][[:space:]]*(type)[[:space:]][[:space:]]*=/ ) {
if ($3 ~ "object") {
print " type = \"object\""
} else {
# Convert multiline stuff into single line
if ($3 ~ /^[[:space:]]*list[[:space:]]*\([[:space:]]*$/) {
type = "list"
} else if ($3 ~ /^[[:space:]]*string[[:space:]]*\([[:space:]]*$/) {
type = "string"
} else if ($3 ~ /^[[:space:]]*map[[:space:]]*\([[:space:]]*$/) {
type = "map"
} else {
type = $3
}
# legacy quoted types: "string", "list", and "map"
if (type ~ /^[[:space:]]*"(.*?)"[[:space:]]*$/) {
print " type = " type
} else {
print " type = \"" type "\""
}
}
# Count opening blocks
blockTypeCnt += gsub(/\(/, "")
blockTypeCnt += gsub(/\[/, "")
blockTypeCnt += gsub(/\{/, "")
# Count closing blocks
blockTypeCnt -= gsub(/\)/, "")
blockTypeCnt -= gsub(/\]/, "")
blockTypeCnt -= gsub(/\}/, "")
}
}
# ----------------------------------------------------------------------------------------------
# description = ...
# ----------------------------------------------------------------------------------------------
# [PRINT] single line "description"
if (blockCnt > 0 && blockTypeCnt == 0 && blockDefaultCnt == 0) {
if ($0 ~ /^[[:space:]][[:space:]]*description[[:space:]][[:space:]]*=/) {
print $0
}
}
# ----------------------------------------------------------------------------------------------
# value = ...
# ----------------------------------------------------------------------------------------------
## [PRINT] single line "value"
#if (blockCnt > 0 && blockTypeCnt == 0 && blockDefaultCnt == 0) {
# if ($0 ~ /^[[:space:]][[:space:]]*value[[:space:]][[:space:]]*=/) {
# print $0
# }
#}
# ----------------------------------------------------------------------------------------------
# Newlines, comments, everything else
# ----------------------------------------------------------------------------------------------
#if (blockTypeCnt == 0 && blockDefaultCnt == 0) {
# Comments with '#'
if ($0 ~ /^[[:space:]]*#/) {
print $0
}
# Comments with '//'
if ($0 ~ /^[[:space:]]*\/\//) {
print $0
}
# Newlines
if ($0 ~ /^[[:space:]]*$/) {
print $0
}
#}
}
EOF

}

[ "${BASH_SOURCE[0]}" != "$0" ] || main "$@"
Loading