2018-05-16 20:04:48 +02:00
#!/usr/bin/env bash
2020-08-27 20:55:28 +01:00
set -eo pipefail
2018-05-16 20:04:48 +02:00
2018-11-13 12:30:06 +01:00
main( ) {
2020-08-27 20:55:28 +01:00
initialize_
parse_cmdline_ " $@ "
2020-09-07 14:50:57 +01:00
terraform_docs_ " ${ ARGS [*] } " " ${ FILES [@] } "
2020-08-27 20:55:28 +01:00
}
initialize_( ) {
# get directory containing this script
local dir
local source
source = " ${ BASH_SOURCE [0] } "
while [ [ -L $source ] ] ; do # resolve $source until the file is no longer a symlink
dir = " $( cd -P " $( dirname " $source " ) " > /dev/null && pwd ) "
source = " $( readlink " $source " ) "
# if $source was a relative symlink, we need to resolve it relative to the path where the symlink file was located
[ [ $source != /* ] ] && source = " $dir / $source "
done
_SCRIPT_DIR = " $( dirname " $source " ) "
# source getopt function
# shellcheck source=lib_getopt
. " $_SCRIPT_DIR /lib_getopt "
}
parse_cmdline_( ) {
2018-11-13 12:30:06 +01:00
declare argv
argv = $( getopt -o a: --long args: -- " $@ " ) || return
eval " set -- $argv "
for argv; do
case $argv in
2020-01-21 11:54:13 +01:00
-a | --args)
2018-11-13 12:30:06 +01:00
shift
2020-08-27 20:55:28 +01:00
ARGS += ( " $1 " )
2018-11-13 12:30:06 +01:00
shift
; ;
2020-01-21 11:54:13 +01:00
--)
2018-11-13 12:30:06 +01:00
shift
2020-08-27 20:55:28 +01:00
FILES = ( " $@ " )
2018-11-13 12:30:06 +01:00
break
; ;
esac
done
2020-08-27 20:55:28 +01:00
}
terraform_docs_( ) {
local -r args = " $1 "
2020-09-07 14:50:57 +01:00
shift
local -a -r files = ( " $@ " )
2018-11-13 12:30:06 +01:00
2020-01-21 05:19:46 -05:00
local hack_terraform_docs
2020-09-24 11:37:08 +02:00
hack_terraform_docs = $( terraform version | sed -n 1p | grep -c 0.12) || true
2019-06-17 12:47:06 +02:00
2020-01-21 05:19:46 -05:00
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
2020-09-24 14:44:05 -05:00
is_old_terraform_docs = $( terraform-docs version | grep -o "v0.[1-7]\." | tail -1) || true
2020-01-21 05:19:46 -05:00
2020-01-21 11:54:13 +01:00
if [ [ -z " $is_old_terraform_docs " ] ] ; then # Using terraform-docs 0.8+ (preferred)
2020-01-21 05:19:46 -05:00
2020-09-07 14:50:57 +01:00
terraform_docs "0" " $args " " ${ files [@] } "
2020-01-21 05:19:46 -05:00
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
2019-06-17 12:47:06 +02:00
2020-08-27 20:55:28 +01:00
local tmp_file_awk
2019-06-18 21:17:57 +02:00
tmp_file_awk = $( mktemp " ${ TMPDIR :- /tmp } /terraform-docs-XXXXXXXXXX " )
2019-06-18 13:58:49 +02:00
terraform_docs_awk " $tmp_file_awk "
2020-09-07 14:50:57 +01:00
terraform_docs " $tmp_file_awk " " $args " " ${ files [@] } "
2019-06-18 13:58:49 +02:00
rm -f " $tmp_file_awk "
2020-01-21 05:19:46 -05:00
else # Using terraform 0.11 and no awk script is needed for that
2020-09-07 14:50:57 +01:00
terraform_docs "0" " $args " " ${ files [@] } "
2019-06-17 12:47:06 +02:00
2020-01-21 05:19:46 -05:00
fi
2018-11-13 12:30:06 +01:00
}
terraform_docs( ) {
2020-08-27 20:55:28 +01:00
local -r terraform_docs_awk_file = " $1 "
local -r args = " $2 "
2020-09-07 14:50:57 +01:00
shift 2
local -a -r files = ( " $@ " )
2018-11-13 12:30:06 +01:00
declare -a paths
declare -a tfvars_files
2020-08-27 20:55:28 +01:00
local index = 0
local file_with_path
2020-09-07 14:50:57 +01:00
for file_with_path in " ${ files [@] } " ; do
2018-11-13 12:30:06 +01:00
file_with_path = " ${ file_with_path // /__REPLACED__SPACE__ } "
2018-05-16 20:04:48 +02:00
2018-11-13 12:30:06 +01:00
paths[ index] = $( dirname " $file_with_path " )
2018-05-16 20:04:48 +02:00
2018-11-13 12:30:06 +01:00
if [ [ " $file_with_path " = = *".tfvars" ] ] ; then
tfvars_files += ( " $file_with_path " )
fi
2018-05-16 20:04:48 +02:00
2020-01-21 11:54:13 +01:00
( ( index += 1) )
2018-11-13 12:30:06 +01:00
done
2018-05-16 20:04:48 +02:00
2020-08-27 20:55:28 +01:00
local -r tmp_file = $( mktemp)
local -r text_file = "README.md"
2018-05-16 20:04:48 +02:00
2020-08-27 20:55:28 +01:00
local path_uniq
2018-11-13 12:30:06 +01:00
for path_uniq in $( echo " ${ paths [*] } " | tr ' ' '\n' | sort -u) ; do
path_uniq = " ${ path_uniq //__REPLACED__SPACE__/ } "
2018-05-16 20:04:48 +02:00
2018-11-13 12:30:06 +01:00
pushd " $path_uniq " > /dev/null
2018-05-16 20:04:48 +02:00
2018-11-13 12:30:06 +01:00
if [ [ ! -f " $text_file " ] ] ; then
popd > /dev/null
continue
fi
2018-05-16 20:04:48 +02:00
2020-01-21 11:54:13 +01:00
if [ [ " $terraform_docs_awk_file " = = "0" ] ] ; then
2020-04-04 06:55:01 +01:00
# shellcheck disable=SC2086
terraform-docs md $args ./ > " $tmp_file "
2020-01-21 11:54:13 +01:00
else
# Can't append extension for mktemp, so renaming instead
2020-08-27 20:55:28 +01:00
local tmp_file_docs
2020-01-21 11:54:13 +01:00
tmp_file_docs = $( mktemp " ${ TMPDIR :- /tmp } /terraform-docs-XXXXXXXXXX " )
mv " $tmp_file_docs " " $tmp_file_docs .tf "
2020-08-27 20:55:28 +01:00
local tmp_file_docs_tf
2020-01-21 11:54:13 +01:00
tmp_file_docs_tf = " $tmp_file_docs .tf "
awk -f " $terraform_docs_awk_file " ./*.tf > " $tmp_file_docs_tf "
2020-04-04 06:55:01 +01:00
# shellcheck disable=SC2086
terraform-docs md $args " $tmp_file_docs_tf " > " $tmp_file "
2020-01-21 11:54:13 +01:00
rm -f " $tmp_file_docs_tf "
fi
2018-11-13 12:30:06 +01:00
# 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 -i -ne 'if (/BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK/../END OF PRE-COMMIT-TERRAFORM DOCS HOOK/) { print $_ if /BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK/; print "I_WANT_TO_BE_REPLACED\n$_" if /END OF PRE-COMMIT-TERRAFORM DOCS HOOK/;} else { print $_ }' " $text_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 $_};}' " $text_file "
rm -f " $tmp_file "
2018-05-16 20:04:48 +02:00
2018-05-16 21:58:40 +02:00
popd > /dev/null
2018-11-13 12:30:06 +01:00
done
}
2019-06-17 12:47:06 +02:00
terraform_docs_awk( ) {
2020-08-27 20:55:28 +01:00
local -r output_file = $1
2019-06-17 12:47:06 +02:00
2020-01-21 11:54:13 +01:00
cat << "EOF" > " $output_file "
2019-06-17 12:47:06 +02:00
# 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.
2020-08-19 06:06:55 -04:00
# https://github.com/terraform-docs/terraform-docs/
# https://github.com/terraform-docs/terraform-docs/issues/62
2019-06-18 13:58:49 +02:00
# Script was originally found here: https://github.com/cloudposse/build-harness/blob/master/bin/terraform-docs.awk
2019-06-17 12:47:06 +02:00
{
2019-06-18 13:58:49 +02:00
if ( $0 ~ /\{ / ) {
2019-06-17 12:47:06 +02:00
braceCnt++
}
2019-06-18 13:58:49 +02:00
if ( $0 ~ /\} / ) {
2019-06-17 12:47:06 +02:00
braceCnt--
}
2019-11-02 12:15:33 +01:00
# ----------------------------------------------------------------------------------------------
# variable|output "..." {
# ----------------------------------------------------------------------------------------------
# [END] variable/output block
if ( blockCnt > 0 && blockTypeCnt = = 0 && blockDefaultCnt = = 0) {
if ( braceCnt = = 0 && blockCnt > 0) {
blockCnt--
print $0
}
}
2019-06-17 12:47:06 +02:00
# [START] variable or output block started
2019-06-18 13:58:49 +02:00
if ( $0 ~ /^[ [ :space:] ] *( variable| output) [ [ :space:] ] [ [ :space:] ] *"(.*?)" /) {
2019-11-02 12:15:33 +01:00
# Normalize the braceCnt and block (should be 1 now)
2019-06-18 13:58:49 +02:00
braceCnt = 1
2019-11-02 12:15:33 +01:00
blockCnt = 1
# [CLOSE] "default" and "type" block
blockDefaultCnt = 0
blockTypeCnt = 0
# Print variable|output line
2019-06-17 12:47:06 +02:00
print $0
}
2019-11-02 12:15:33 +01:00
# ----------------------------------------------------------------------------------------------
# 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) {
2019-06-18 13:58:49 +02:00
if ( $0 ~ /^[ [ :space:] ] [ [ :space:] ] *( default) [ [ :space:] ] [ [ :space:] ] *= /) {
if ( $3 ~ "null" ) {
print " default = \"null\""
} else {
print $0
2019-11-02 12:15:33 +01:00
# Count opening blocks
blockDefaultCnt += gsub( /\( /, "" )
blockDefaultCnt += gsub( /\[ /, "" )
blockDefaultCnt += gsub( /\{ /, "" )
# Count closing blocks
blockDefaultCnt -= gsub( /\) /, "" )
blockDefaultCnt -= gsub( /\] /, "" )
blockDefaultCnt -= gsub( /\} /, "" )
2019-06-17 12:47:06 +02:00
}
}
}
2019-11-02 12:15:33 +01:00
# ----------------------------------------------------------------------------------------------
# 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" ) {
2019-06-17 12:47:06 +02:00
print " type = \"object\""
} else {
2019-11-02 12:15:33 +01:00
# 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
}
2019-06-25 14:34:46 +02:00
# legacy quoted types: "string", "list", and "map"
2019-11-02 12:15:33 +01:00
if ( type ~ /^[ [ :space:] ] *"(.*?)" [ [ :space:] ] *$/) {
print " type = " type
2019-06-25 14:34:46 +02:00
} else {
2019-11-02 12:15:33 +01:00
print " type = \"" type "\""
2019-06-25 14:34:46 +02:00
}
2019-06-17 12:47:06 +02:00
}
2019-11-02 12:15:33 +01:00
# Count opening blocks
blockTypeCnt += gsub( /\( /, "" )
blockTypeCnt += gsub( /\[ /, "" )
blockTypeCnt += gsub( /\{ /, "" )
# Count closing blocks
blockTypeCnt -= gsub( /\) /, "" )
blockTypeCnt -= gsub( /\] /, "" )
blockTypeCnt -= gsub( /\} /, "" )
2019-06-17 12:47:06 +02:00
}
}
2019-11-02 12:15:33 +01:00
# ----------------------------------------------------------------------------------------------
# description = ...
# ----------------------------------------------------------------------------------------------
# [PRINT] single line "description"
if ( blockCnt > 0 && blockTypeCnt = = 0 && blockDefaultCnt = = 0) {
if ( $0 ~ /^[ [ :space:] ] [ [ :space:] ] *description[ [ :space:] ] [ [ :space:] ] *= /) {
2019-06-17 12:47:06 +02:00
print $0
}
}
2019-11-02 12:15:33 +01:00
# ----------------------------------------------------------------------------------------------
# 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
2019-06-17 12:47:06 +02:00
}
2019-11-02 12:15:33 +01:00
#}
2019-06-17 12:47:06 +02:00
}
EOF
}
2020-11-02 21:44:54 +01:00
# global arrays
2020-08-27 20:55:28 +01:00
declare -a ARGS = ( )
declare -a FILES = ( )
2018-05-16 20:04:48 +02:00
2020-08-27 20:55:28 +01:00
[ [ ${ BASH_SOURCE [0] } != " $0 " ] ] || main " $@ "