From 97a668640d21b76e5afa727bb5af005fe9501bcb Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Wed, 16 May 2018 20:04:48 +0200 Subject: [PATCH 001/214] Added terraform-docs integration (#13) * Add hook to create readme * Updated README --- .pre-commit-hooks.yaml | 8 ++++++++ README.md | 16 ++++++++++++---- hooks.yaml | 8 ++++++++ terraform_docs.sh | 39 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 67 insertions(+), 4 deletions(-) create mode 100755 terraform_docs.sh diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 786ef5a56..842faa94b 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -6,6 +6,14 @@ files: (\.tf|\.tfvars)$ exclude: \.terraform\/.*$ +- id: terraform_docs + name: Terraform Docs + description: Inserts input and output documentation into README.md (using terraform-docs). + entry: terraform_docs.sh + language: script + files: (\.tf)$ + exclude: \.terraform\/.*$ + - id: terraform_validate_no_variables name: Terraform validate without variables description: Validates all Terraform configuration files without checking whether all required variables were set (basic check). diff --git a/README.md b/README.md index 8a3fa373f..867feeb40 100644 --- a/README.md +++ b/README.md @@ -6,16 +6,24 @@ Several [pre-commit](http://pre-commit.com/) hooks to keep Terraform configurati * `terraform_fmt` - Rewrites all Terraform configuration files to a canonical format. * `terraform_validate_no_variables` - Validates all Terraform configuration files without checking whether all required variables were set. * `terraform_validate_with_variables` - Validates all Terraform configuration files and checks whether all required variables were specified. +* `terraform_docs` - Inserts input and output documentation into `README.md`. -Note that `terraform_validate_no_variables` and `terraform_validate_with_variables` will not work if variables are being set dynamically (eg, when using [Terragrunt](https://github.com/gruntwork-io/terragrunt)). Use `terragrunt validate` command instead. +## Notes about hooks -An example `.pre-commit-config.yaml`: +1. `terraform_validate_no_variables` and `terraform_validate_with_variables` will not work if variables are being set dynamically (eg, when using [Terragrunt](https://github.com/gruntwork-io/terragrunt)). Use `terragrunt validate` command instead. + +1. `terraform_docs` will insert/update documentation generated by [terraform-docs](https://github.com/segmentio/terraform-docs) between markers - `` and `` if they are present in `README.md`. Make sure that `terraform-docs` is installed. + +## Example + +`.pre-commit-config.yaml`: ```yaml - repo: git://github.com/antonbabenko/pre-commit-terraform - sha: v1.6.0 + rev: v1.7.0 hooks: - id: terraform_fmt + - id: terraform_docs ``` -Enjoy the clean code! +Enjoy the clean and documented code! diff --git a/hooks.yaml b/hooks.yaml index 786ef5a56..842faa94b 100644 --- a/hooks.yaml +++ b/hooks.yaml @@ -6,6 +6,14 @@ files: (\.tf|\.tfvars)$ exclude: \.terraform\/.*$ +- id: terraform_docs + name: Terraform Docs + description: Inserts input and output documentation into README.md (using terraform-docs). + entry: terraform_docs.sh + language: script + files: (\.tf)$ + exclude: \.terraform\/.*$ + - id: terraform_validate_no_variables name: Terraform validate without variables description: Validates all Terraform configuration files without checking whether all required variables were set (basic check). diff --git a/terraform_docs.sh b/terraform_docs.sh new file mode 100755 index 000000000..a3008aeaa --- /dev/null +++ b/terraform_docs.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +set -e + +declare -a paths +declare -a tfvars_files + +index=0 + +for file_with_path in "$@"; do + file_with_path="${file_with_path// /__REPLACED__SPACE__}" + + paths[index]=$(dirname "$file_with_path") + + if [[ "$file_with_path" == *".tfvars" ]]; then + tfvars_files+=("$file_with_path") + fi + + let "index+=1" +done + +readonly tmp_file="tmp_$(date | md5).txt" + +for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do + path_uniq="${path_uniq//__REPLACED__SPACE__/ }" + + pushd "$path_uniq" > /dev/null + + terraform-docs md ./ > "$tmp_file" + + # Replace content between markers with the placeholder - http://fahdshariff.blogspot.no/2012/12/sed-mutli-line-replacement-between-two.html + sed -i -n '/BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK/{p;:a;N;/END OF PRE-COMMIT-TERRAFORM DOCS HOOK/!ba;s/.*\n/I_WANT_TO_BE_REPLACED\n/};p' README.md + + # Replace placeholder with the content of the file - https://stackoverflow.com/a/31057013/550451 + sed -i -e "/I_WANT_TO_BE_REPLACED/r $tmp_file" -e "//d" README.md + + rm -f "$tmp_file" + + popd > /dev/null +done From 8e03aec3d5362eb511043c4be0bbc671de5c1ad1 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Wed, 16 May 2018 21:57:49 +0200 Subject: [PATCH 002/214] Run terraform_docs only if README.md is present --- terraform_docs.sh | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/terraform_docs.sh b/terraform_docs.sh index a3008aeaa..4d609b6f9 100755 --- a/terraform_docs.sh +++ b/terraform_docs.sh @@ -19,19 +19,24 @@ for file_with_path in "$@"; do done readonly tmp_file="tmp_$(date | md5).txt" +readonly text_file="README.md" for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do path_uniq="${path_uniq//__REPLACED__SPACE__/ }" pushd "$path_uniq" > /dev/null + if [[ ! -f "$text_file" ]]; then + continue + fi + terraform-docs md ./ > "$tmp_file" # Replace content between markers with the placeholder - http://fahdshariff.blogspot.no/2012/12/sed-mutli-line-replacement-between-two.html - sed -i -n '/BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK/{p;:a;N;/END OF PRE-COMMIT-TERRAFORM DOCS HOOK/!ba;s/.*\n/I_WANT_TO_BE_REPLACED\n/};p' README.md + sed -i -n '/BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK/{p;:a;N;/END OF PRE-COMMIT-TERRAFORM DOCS HOOK/!ba;s/.*\n/I_WANT_TO_BE_REPLACED\n/};p' "$text_file" # Replace placeholder with the content of the file - https://stackoverflow.com/a/31057013/550451 - sed -i -e "/I_WANT_TO_BE_REPLACED/r $tmp_file" -e "//d" README.md + sed -i -e "/I_WANT_TO_BE_REPLACED/r $tmp_file" -e "//d" "$text_file" rm -f "$tmp_file" From 091f8b15d7b458e5a0aca642483deb2205e7db02 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Wed, 16 May 2018 21:58:40 +0200 Subject: [PATCH 003/214] Run terraform_docs only if README.md is present --- terraform_docs.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/terraform_docs.sh b/terraform_docs.sh index 4d609b6f9..fba5730fd 100755 --- a/terraform_docs.sh +++ b/terraform_docs.sh @@ -27,6 +27,7 @@ for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do pushd "$path_uniq" > /dev/null if [[ ! -f "$text_file" ]]; then + popd > /dev/null continue fi From 6b06683c0db2bcee7d9230f107855223c199bea8 Mon Sep 17 00:00:00 2001 From: jeremy avnet <162998+brainsik@users.noreply.github.com> Date: Sun, 20 May 2018 01:00:34 -0700 Subject: [PATCH 004/214] Fixes use of md5 for tempfile name (#16) --- terraform_docs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform_docs.sh b/terraform_docs.sh index fba5730fd..918d5583f 100755 --- a/terraform_docs.sh +++ b/terraform_docs.sh @@ -18,7 +18,7 @@ for file_with_path in "$@"; do let "index+=1" done -readonly tmp_file="tmp_$(date | md5).txt" +readonly tmp_file=$(mktemp) readonly text_file="README.md" for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do From 2d3782cefadb0941102fe413ab08a75905fcd081 Mon Sep 17 00:00:00 2001 From: jeremy avnet <162998+brainsik@users.noreply.github.com> Date: Sun, 20 May 2018 03:10:28 -0700 Subject: [PATCH 005/214] Replace terraform_docs use of GNU sed with perl (#15) * Fix ShellCheck warning 2219 https://github.com/koalaman/shellcheck/wiki/SC2219 * Replace GNU sed commands with perl This replaces the sed commands which required GNU sed be installed with perl versions. This should make this script more universally usable (e.g., on macOS) without installing additional tools. --- terraform_docs.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/terraform_docs.sh b/terraform_docs.sh index 918d5583f..781973aa7 100755 --- a/terraform_docs.sh +++ b/terraform_docs.sh @@ -15,7 +15,7 @@ for file_with_path in "$@"; do tfvars_files+=("$file_with_path") fi - let "index+=1" + ((index+=1)) done readonly tmp_file=$(mktemp) @@ -33,11 +33,11 @@ for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do terraform-docs md ./ > "$tmp_file" - # Replace content between markers with the placeholder - http://fahdshariff.blogspot.no/2012/12/sed-mutli-line-replacement-between-two.html - sed -i -n '/BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK/{p;:a;N;/END OF PRE-COMMIT-TERRAFORM DOCS HOOK/!ba;s/.*\n/I_WANT_TO_BE_REPLACED\n/};p' "$text_file" + # 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 - https://stackoverflow.com/a/31057013/550451 - sed -i -e "/I_WANT_TO_BE_REPLACED/r $tmp_file" -e "//d" "$text_file" + # Replace placeholder with the content of the file + perl -i -e 'open(F, "'"$tmp_file"'"); $f = join "", ; while(<>){if (/I_WANT_TO_BE_REPLACED/) {print $f} else {print $_};}' "$text_file" rm -f "$tmp_file" From 69039c3a8cadd12435040a2161fde0453de7b69a Mon Sep 17 00:00:00 2001 From: Robin Bowes Date: Thu, 24 May 2018 21:10:49 +0100 Subject: [PATCH 006/214] Only run validate if .tf files exist in the directory. (#20) * Only run validate if .tf files exist in the directory. * Same fix, different script :) --- terraform_validate_no_variables.sh | 14 +++++++------- terraform_validate_with_variables.sh | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/terraform_validate_no_variables.sh b/terraform_validate_no_variables.sh index 6a43177a7..2e190448a 100755 --- a/terraform_validate_no_variables.sh +++ b/terraform_validate_no_variables.sh @@ -8,19 +8,19 @@ for file_with_path in "$@"; do file_with_path="${file_with_path// /__REPLACED__SPACE__}" paths[index]=$(dirname "$file_with_path") - let "index+=1" + (( "index+=1" )) done for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do path_uniq="${path_uniq//__REPLACED__SPACE__/ }" pushd "$path_uniq" > /dev/null - terraform validate -check-variables=false - - if [[ "$?" -ne 0 ]]; then - echo - echo "Failed path: $path_uniq" - echo "================================" + if [[ -n "$(find . -maxdepth 1 -name '*.tf' -print -quit)" ]] ; then + if ! terraform validate -check-variables=false ; then + echo + echo "Failed path: $path_uniq" + echo "================================" + fi fi popd > /dev/null done diff --git a/terraform_validate_with_variables.sh b/terraform_validate_with_variables.sh index 42f1b5452..706443ade 100755 --- a/terraform_validate_with_variables.sh +++ b/terraform_validate_with_variables.sh @@ -8,19 +8,19 @@ for file_with_path in "$@"; do file_with_path="${file_with_path// /__REPLACED__SPACE__}" paths[index]=$(dirname "$file_with_path") - let "index+=1" + (( "index+=1" )) done for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do path_uniq="${path_uniq//__REPLACED__SPACE__/ }" pushd "$path_uniq" > /dev/null - terraform validate -check-variables=true - - if [[ "$?" -ne 0 ]]; then - echo - echo "Failed path: $path_uniq" - echo "================================" + if [[ -n "$(find . -maxdepth 1 -name '*.tf' -print -quit)" ]] ; then + if ! terraform validate -check-variables=true ; then + echo + echo "Failed path: $path_uniq" + echo "================================" + fi fi popd > /dev/null done From 8dd603be09855246e7622681e609c1e4dc5513d3 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Thu, 24 May 2018 22:12:04 +0200 Subject: [PATCH 007/214] Updated README --- .pre-commit-hooks.yaml | 2 +- README.md | 2 +- hooks.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 842faa94b..2fda38ba1 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -7,7 +7,7 @@ exclude: \.terraform\/.*$ - id: terraform_docs - name: Terraform Docs + name: Terraform docs description: Inserts input and output documentation into README.md (using terraform-docs). entry: terraform_docs.sh language: script diff --git a/README.md b/README.md index 867feeb40..ce242a35c 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Several [pre-commit](http://pre-commit.com/) hooks to keep Terraform configurati ```yaml - repo: git://github.com/antonbabenko/pre-commit-terraform - rev: v1.7.0 + rev: v1.7.3 hooks: - id: terraform_fmt - id: terraform_docs diff --git a/hooks.yaml b/hooks.yaml index 842faa94b..2fda38ba1 100644 --- a/hooks.yaml +++ b/hooks.yaml @@ -7,7 +7,7 @@ exclude: \.terraform\/.*$ - id: terraform_docs - name: Terraform Docs + name: Terraform docs description: Inserts input and output documentation into README.md (using terraform-docs). entry: terraform_docs.sh language: script From b7c50947b0f3968172a9c5a4df2ad3bb203dca22 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Tue, 10 Jul 2018 11:28:43 +0200 Subject: [PATCH 008/214] Added license file (fixed #21) --- LICENSE | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..2755ae9bc --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2017 Anton Babenko + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. From fbdd40ac8f8ebbcaecc1f90e762de753b1aab79c Mon Sep 17 00:00:00 2001 From: Martin Etmajer Date: Tue, 13 Nov 2018 12:30:06 +0100 Subject: [PATCH 009/214] Add feature to pass options to terraform-docs. --- terraform_docs.sh | 579 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 552 insertions(+), 27 deletions(-) diff --git a/terraform_docs.sh b/terraform_docs.sh index 781973aa7..1bd79627a 100755 --- a/terraform_docs.sh +++ b/terraform_docs.sh @@ -1,45 +1,570 @@ #!/usr/bin/env bash + set -e -declare -a paths -declare -a tfvars_files +main() { + declare argv + argv=$(getopt -o a: --long args: -- "$@") || return + eval "set -- $argv" + + declare args + declare files + + for argv; do + case $argv in + (-a|--args) + shift + args="$1" + shift + ;; + (--) + shift + files="$@" + break + ;; + esac + done + + terraform_docs "$args" "$files" +} + +terraform_docs() { + readonly args="$1" + readonly files="$2" + + declare -a paths + declare -a tfvars_files + + index=0 + + for file_with_path in $files; do + file_with_path="${file_with_path// /__REPLACED__SPACE__}" -index=0 + paths[index]=$(dirname "$file_with_path") -for file_with_path in "$@"; do - file_with_path="${file_with_path// /__REPLACED__SPACE__}" + if [[ "$file_with_path" == *".tfvars" ]]; then + tfvars_files+=("$file_with_path") + fi - paths[index]=$(dirname "$file_with_path") + ((index+=1)) + done - if [[ "$file_with_path" == *".tfvars" ]]; then - tfvars_files+=("$file_with_path") - fi + readonly tmp_file=$(mktemp) + readonly text_file="README.md" - ((index+=1)) -done + for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do + path_uniq="${path_uniq//__REPLACED__SPACE__/ }" -readonly tmp_file=$(mktemp) -readonly text_file="README.md" + pushd "$path_uniq" > /dev/null -for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do - path_uniq="${path_uniq//__REPLACED__SPACE__/ }" + if [[ ! -f "$text_file" ]]; then + popd > /dev/null + continue + fi - pushd "$path_uniq" > /dev/null + terraform-docs $args md ./ > "$tmp_file" + + # 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 "", ; while(<>){if (/I_WANT_TO_BE_REPLACED/) {print $f} else {print $_};}' "$text_file" + + rm -f "$tmp_file" - if [[ ! -f "$text_file" ]]; then popd > /dev/null - continue - fi + done +} + +getopt() { + # pure-getopt, a drop-in replacement for GNU getopt in pure Bash. + # version 1.4.3 + # + # Copyright 2012-2018 Aron Griffis + # + # Permission is hereby granted, free of charge, to any person obtaining + # a copy of this software and associated documentation files (the + # "Software"), to deal in the Software without restriction, including + # without limitation the rights to use, copy, modify, merge, publish, + # distribute, sublicense, and/or sell copies of the Software, and to + # permit persons to whom the Software is furnished to do so, subject to + # the following conditions: + # + # The above copyright notice and this permission notice shall be included + # in all copies or substantial portions of the Software. + # + # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + _getopt_main() { + # Returns one of the following statuses: + # 0 success + # 1 error parsing parameters + # 2 error in getopt invocation + # 3 internal error + # 4 reserved for -T + # + # For statuses 0 and 1, generates normalized and shell-quoted + # "options -- parameters" on stdout. + + declare parsed status + declare short long name flags + declare have_short=false + + # Synopsis from getopt man-page: + # + # getopt optstring parameters + # getopt [options] [--] optstring parameters + # getopt [options] -o|--options optstring [options] [--] parameters + # + # The first form can be normalized to the third form which + # _getopt_parse() understands. The second form can be recognized after + # first parse when $short hasn't been set. + + if [[ -n ${GETOPT_COMPATIBLE+isset} || $1 == [^-]* ]]; then + # Enable compatibility mode + flags=c$flags + # Normalize first to third synopsis form + set -- -o "$1" -- "${@:2}" + fi + + # First parse always uses flags=p since getopt always parses its own + # arguments effectively in this mode. + parsed=$(_getopt_parse getopt ahl:n:o:qQs:TuV \ + alternative,help,longoptions:,name:,options:,quiet,quiet-output,shell:,test,version \ + p "$@") + status=$? + if [[ $status != 0 ]]; then + if [[ $status == 1 ]]; then + echo "Try \`getopt --help' for more information." >&2 + # Since this is the first parse, convert status 1 to 2 + status=2 + fi + return $status + fi + eval "set -- $parsed" + + while [[ $# -gt 0 ]]; do + case $1 in + (-a|--alternative) + flags=a$flags ;; + + (-h|--help) + _getopt_help + return 2 # as does GNU getopt + ;; + + (-l|--longoptions) + long="$long${long:+,}$2" + shift ;; + + (-n|--name) + name=$2 + shift ;; + + (-o|--options) + short=$2 + have_short=true + shift ;; + + (-q|--quiet) + flags=q$flags ;; + + (-Q|--quiet-output) + flags=Q$flags ;; + + (-s|--shell) + case $2 in + (sh|bash) + flags=${flags//t/} ;; + (csh|tcsh) + flags=t$flags ;; + (*) + echo 'getopt: unknown shell after -s or --shell argument' >&2 + echo "Try \`getopt --help' for more information." >&2 + return 2 ;; + esac + shift ;; + + (-u|--unquoted) + flags=u$flags ;; + + (-T|--test) + return 4 ;; + + (-V|--version) + echo "pure-getopt 1.4.3" + return 0 ;; + + (--) + shift + break ;; + esac + + shift + done + + if ! $have_short; then + # $short was declared but never set, not even to an empty string. + # This implies the second form in the synopsis. + if [[ $# == 0 ]]; then + echo 'getopt: missing optstring argument' >&2 + echo "Try \`getopt --help' for more information." >&2 + return 2 + fi + short=$1 + have_short=true + shift + fi + + if [[ $short == -* ]]; then + # Leading dash means generate output in place rather than reordering, + # unless we're already in compatibility mode. + [[ $flags == *c* ]] || flags=i$flags + short=${short#?} + elif [[ $short == +* ]]; then + # Leading plus means POSIXLY_CORRECT, unless we're already in + # compatibility mode. + [[ $flags == *c* ]] || flags=p$flags + short=${short#?} + fi + + # This should fire if POSIXLY_CORRECT is in the environment, even if + # it's an empty string. That's the difference between :+ and + + flags=${POSIXLY_CORRECT+p}$flags + + _getopt_parse "${name:-getopt}" "$short" "$long" "$flags" "$@" + } + + _getopt_parse() { + # Inner getopt parser, used for both first parse and second parse. + # Returns 0 for success, 1 for error parsing, 3 for internal error. + # In the case of status 1, still generates stdout with whatever could + # be parsed. + # + # $flags is a string of characters with the following meanings: + # a - alternative parsing mode + # c - GETOPT_COMPATIBLE + # i - generate output in place rather than reordering + # p - POSIXLY_CORRECT + # q - disable error reporting + # Q - disable normal output + # t - quote for csh/tcsh + # u - unquoted output + + declare name="$1" short="$2" long="$3" flags="$4" + shift 4 + + # Split $long on commas, prepend double-dashes, strip colons; + # for use with _getopt_resolve_abbrev + declare -a longarr + _getopt_split longarr "$long" + longarr=( "${longarr[@]/#/--}" ) + longarr=( "${longarr[@]%:}" ) + longarr=( "${longarr[@]%:}" ) + + # Parse and collect options and parameters + declare -a opts params + declare o alt_recycled=false error=0 + + while [[ $# -gt 0 ]]; do + case $1 in + (--) + params=( "${params[@]}" "${@:2}" ) + break ;; + + (--*=*) + o=${1%%=*} + if ! o=$(_getopt_resolve_abbrev "$o" "${longarr[@]}"); then + error=1 + elif [[ ,"$long", == *,"${o#--}"::,* ]]; then + opts=( "${opts[@]}" "$o" "${1#*=}" ) + elif [[ ,"$long", == *,"${o#--}":,* ]]; then + opts=( "${opts[@]}" "$o" "${1#*=}" ) + elif [[ ,"$long", == *,"${o#--}",* ]]; then + if $alt_recycled; then o=${o#-}; fi + _getopt_err "$name: option '$o' doesn't allow an argument" + error=1 + else + echo "getopt: assertion failed (1)" >&2 + return 3 + fi + alt_recycled=false + ;; + + (--?*) + o=$1 + if ! o=$(_getopt_resolve_abbrev "$o" "${longarr[@]}"); then + error=1 + elif [[ ,"$long", == *,"${o#--}",* ]]; then + opts=( "${opts[@]}" "$o" ) + elif [[ ,"$long", == *,"${o#--}::",* ]]; then + opts=( "${opts[@]}" "$o" '' ) + elif [[ ,"$long", == *,"${o#--}:",* ]]; then + if [[ $# -ge 2 ]]; then + shift + opts=( "${opts[@]}" "$o" "$1" ) + else + if $alt_recycled; then o=${o#-}; fi + _getopt_err "$name: option '$o' requires an argument" + error=1 + fi + else + echo "getopt: assertion failed (2)" >&2 + return 3 + fi + alt_recycled=false + ;; + + (-*) + if [[ $flags == *a* ]]; then + # Alternative parsing mode! + # Try to handle as a long option if any of the following apply: + # 1. There's an equals sign in the mix -x=3 or -xy=3 + # 2. There's 2+ letters and an abbreviated long match -xy + # 3. There's a single letter and an exact long match + # 4. There's a single letter and no short match + o=${1::2} # temp for testing #4 + if [[ $1 == *=* || $1 == -?? || \ + ,$long, == *,"${1#-}"[:,]* || \ + ,$short, != *,"${o#-}"[:,]* ]]; then + o=$(_getopt_resolve_abbrev "${1%%=*}" "${longarr[@]}" 2>/dev/null) + case $? in + (0) + # Unambiguous match. Let the long options parser handle + # it, with a flag to get the right error message. + set -- "-$1" "${@:2}" + alt_recycled=true + continue ;; + (1) + # Ambiguous match, generate error and continue. + _getopt_resolve_abbrev "${1%%=*}" "${longarr[@]}" >/dev/null + error=1 + shift + continue ;; + (2) + # No match, fall through to single-character check. + true ;; + (*) + echo "getopt: assertion failed (3)" >&2 + return 3 ;; + esac + fi + fi + + o=${1::2} + if [[ "$short" == *"${o#-}"::* ]]; then + if [[ ${#1} -gt 2 ]]; then + opts=( "${opts[@]}" "$o" "${1:2}" ) + else + opts=( "${opts[@]}" "$o" '' ) + fi + elif [[ "$short" == *"${o#-}":* ]]; then + if [[ ${#1} -gt 2 ]]; then + opts=( "${opts[@]}" "$o" "${1:2}" ) + elif [[ $# -ge 2 ]]; then + shift + opts=( "${opts[@]}" "$o" "$1" ) + else + _getopt_err "$name: option requires an argument -- '${o#-}'" + error=1 + fi + elif [[ "$short" == *"${o#-}"* ]]; then + opts=( "${opts[@]}" "$o" ) + if [[ ${#1} -gt 2 ]]; then + set -- "$o" "-${1:2}" "${@:2}" + fi + else + if [[ $flags == *a* ]]; then + # Alternative parsing mode! Report on the entire failed + # option. GNU includes =value but we omit it for sanity with + # very long values. + _getopt_err "$name: unrecognized option '${1%%=*}'" + else + _getopt_err "$name: invalid option -- '${o#-}'" + if [[ ${#1} -gt 2 ]]; then + set -- "$o" "-${1:2}" "${@:2}" + fi + fi + error=1 + fi ;; + + (*) + # GNU getopt in-place mode (leading dash on short options) + # overrides POSIXLY_CORRECT + if [[ $flags == *i* ]]; then + opts=( "${opts[@]}" "$1" ) + elif [[ $flags == *p* ]]; then + params=( "${params[@]}" "$@" ) + break + else + params=( "${params[@]}" "$1" ) + fi + esac + + shift + done + + if [[ $flags == *Q* ]]; then + true # generate no output + else + echo -n ' ' + if [[ $flags == *[cu]* ]]; then + printf '%s -- %s' "${opts[*]}" "${params[*]}" + else + if [[ $flags == *t* ]]; then + _getopt_quote_csh "${opts[@]}" -- "${params[@]}" + else + _getopt_quote "${opts[@]}" -- "${params[@]}" + fi + fi + echo + fi + + return $error + } + + _getopt_err() { + if [[ $flags != *q* ]]; then + printf '%s\n' "$1" >&2 + fi + } + + _getopt_resolve_abbrev() { + # Resolves an abbrevation from a list of possibilities. + # If the abbreviation is unambiguous, echoes the expansion on stdout + # and returns 0. If the abbreviation is ambiguous, prints a message on + # stderr and returns 1. (For first parse this should convert to exit + # status 2.) If there is no match at all, prints a message on stderr + # and returns 2. + declare a q="$1" + declare -a matches + shift + for a; do + if [[ $q == "$a" ]]; then + # Exact match. Squash any other partial matches. + matches=( "$a" ) + break + elif [[ $flags == *a* && $q == -[^-]* && $a == -"$q" ]]; then + # Exact alternative match. Squash any other partial matches. + matches=( "$a" ) + break + elif [[ $a == "$q"* ]]; then + # Abbreviated match. + matches=( "${matches[@]}" "$a" ) + elif [[ $flags == *a* && $q == -[^-]* && $a == -"$q"* ]]; then + # Abbreviated alternative match. + matches=( "${matches[@]}" "${a#-}" ) + fi + done + case ${#matches[@]} in + (0) + [[ $flags == *q* ]] || \ + printf "$name: unrecognized option %s\\n" >&2 \ + "$(_getopt_quote "$q")" + return 2 ;; + (1) + printf '%s' "${matches[0]}"; return 0 ;; + (*) + [[ $flags == *q* ]] || \ + printf "$name: option %s is ambiguous; possibilities: %s\\n" >&2 \ + "$(_getopt_quote "$q")" "$(_getopt_quote "${matches[@]}")" + return 1 ;; + esac + } + + _getopt_split() { + # Splits $2 at commas to build array specified by $1 + declare IFS=, + eval "$1=( \$2 )" + } + + _getopt_quote() { + # Quotes arguments with single quotes, escaping inner single quotes + declare s space q=\' + for s; do + printf "$space'%s'" "${s//$q/$q\\$q$q}" + space=' ' + done + } + + _getopt_quote_csh() { + # Quotes arguments with single quotes, escaping inner single quotes, + # bangs, backslashes and newlines + declare s i c space + for s; do + echo -n "$space'" + for ((i=0; i<${#s}; i++)); do + c=${s:i:1} + case $c in + (\\|\'|!) + echo -n "'\\$c'" ;; + ($'\n') + echo -n "\\$c" ;; + (*) + echo -n "$c" ;; + esac + done + echo -n \' + space=' ' + done + } + + _getopt_help() { + cat <<-EOT >&2 + + Usage: + getopt + getopt [options] [--] + getopt [options] -o|--options [options] [--] + + Parse command options. + + Options: + -a, --alternative allow long options starting with single - + -l, --longoptions the long options to be recognized + -n, --name the name under which errors are reported + -o, --options the short options to be recognized + -q, --quiet disable error reporting by getopt(3) + -Q, --quiet-output no normal output + -s, --shell set quoting conventions to those of + -T, --test test for getopt(1) version + -u, --unquoted do not quote the output + + -h, --help display this help and exit + -V, --version output version information and exit + + For more details see getopt(1). + EOT + } - terraform-docs md ./ > "$tmp_file" + _getopt_version_check() { + if [[ -z $BASH_VERSION ]]; then + echo "getopt: unknown version of bash might not be compatible" >&2 + return 1 + fi - # 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" + # This is a lexical comparison that should be sufficient forever. + if [[ $BASH_VERSION < 2.05b ]]; then + echo "getopt: bash $BASH_VERSION might not be compatible" >&2 + return 1 + fi - # Replace placeholder with the content of the file - perl -i -e 'open(F, "'"$tmp_file"'"); $f = join "", ; while(<>){if (/I_WANT_TO_BE_REPLACED/) {print $f} else {print $_};}' "$text_file" + return 0 + } - rm -f "$tmp_file" + _getopt_version_check + _getopt_main "$@" + declare status=$? + unset -f _getopt_main _getopt_err _getopt_parse _getopt_quote \ + _getopt_quote_csh _getopt_resolve_abbrev _getopt_split _getopt_help \ + _getopt_version_check + return $status +} - popd > /dev/null -done +[[ $BASH_SOURCE != "$0" ]] || main "$@" From e2760caf8d70875ca85d25b11a972e1f207217f8 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Tue, 11 Dec 2018 20:21:49 +0100 Subject: [PATCH 010/214] Added followup after #25 --- .pre-commit-config.yaml | 2 +- .pre-commit-hooks.yaml | 9 ++++++ README.md | 71 +++++++++++++++++++++++++++++++++-------- hooks.yaml | 9 ++++++ 4 files changed, 77 insertions(+), 14 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 70a21ad98..1c444a95a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: git://github.com/pre-commit/pre-commit-hooks - rev: v1.2.3 + rev: v2.0.0 hooks: - id: check-yaml - id: end-of-file-fixer diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 2fda38ba1..65436d109 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -10,6 +10,15 @@ name: Terraform docs description: Inserts input and output documentation into README.md (using terraform-docs). entry: terraform_docs.sh + args: [--args=--with-aggregate-type-defaults] + language: script + files: (\.tf)$ + exclude: \.terraform\/.*$ + +- id: terraform_docs_without_aggregate_type_defaults + name: Terraform docs (without aggregate type defaults) + description: Inserts input and output documentation into README.md (using terraform-docs). + entry: terraform_docs.sh language: script files: (\.tf)$ exclude: \.terraform\/.*$ diff --git a/README.md b/README.md index ce242a35c..e3e7699ef 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,74 @@ -# pre-commit-terraform hook +# Collection of git hooks for Terraform to be used with [pre-commit framework](http://pre-commit.com/) [![Github tag](https://img.shields.io/github/tag/antonbabenko/pre-commit-terraform.svg)](https://github.com/antonbabenko/pre-commit-terraform/releases) ![](https://img.shields.io/maintenance/yes/2018.svg) [![Help Contribute to Open Source](https://www.codetriage.com/antonbabenko/pre-commit-terraform/badges/users.svg)](https://www.codetriage.com/antonbabenko/pre-commit-terraform) -Several [pre-commit](http://pre-commit.com/) hooks to keep Terraform configurations (both `*.tf` and `*.tfvars`) in a good shape: -* `terraform_fmt` - Rewrites all Terraform configuration files to a canonical format. -* `terraform_validate_no_variables` - Validates all Terraform configuration files without checking whether all required variables were set. -* `terraform_validate_with_variables` - Validates all Terraform configuration files and checks whether all required variables were specified. -* `terraform_docs` - Inserts input and output documentation into `README.md`. +## How to install -## Notes about hooks +### Step 1 -1. `terraform_validate_no_variables` and `terraform_validate_with_variables` will not work if variables are being set dynamically (eg, when using [Terragrunt](https://github.com/gruntwork-io/terragrunt)). Use `terragrunt validate` command instead. +On MacOSX install the pre-commit package + +```bash +brew install pre-commit +``` -1. `terraform_docs` will insert/update documentation generated by [terraform-docs](https://github.com/segmentio/terraform-docs) between markers - `` and `` if they are present in `README.md`. Make sure that `terraform-docs` is installed. +For other operating systems check the [official documentation](http://pre-commit.com/#install) -## Example +### Step 2 -`.pre-commit-config.yaml`: +Step into the repository you want to have the pre-commit hooks installed and run: -```yaml +```bash +cat < .pre-commit-config.yaml - repo: git://github.com/antonbabenko/pre-commit-terraform - rev: v1.7.3 + rev: v1.7.4 hooks: - id: terraform_fmt - id: terraform_docs +EOF +``` + +### Step 3 + +Install the pre-commit hook + +```bash +pre-commit install +``` + +### Step 4 + +After pre-commit hook has been installed you can run it manually on all files in the repository + +```bash +pre-commit run -a ``` +## Available Hooks + +There are several [pre-commit](http://pre-commit.com/) hooks to keep Terraform configurations (both `*.tf` and `*.tfvars`) in a good shape: +* `terraform_fmt` - Rewrites all Terraform configuration files to a canonical format. +* `terraform_validate_no_variables` - Validates all Terraform configuration files without checking whether all required variables were set. +* `terraform_validate_with_variables` - Validates all Terraform configuration files and checks whether all required variables were specified. +* `terraform_docs` - Inserts input and output documentation into `README.md`. Recommended. +* `terraform_docs_without_aggregate_type_defaults` - Inserts input and output documentation into `README.md` without aggregate type defaults. + +Check the [source file](https://github.com/antonbabenko/pre-commit-terraform/blob/master/.pre-commit-hooks.yaml) to know arguments used for each hook. + +## Notes about hooks + +1. `terraform_validate_no_variables` and `terraform_validate_with_variables` will not work if variables are being set dynamically (eg, when using [Terragrunt](https://github.com/gruntwork-io/terragrunt)). Use `terragrunt validate` command instead. + +1. `terraform_docs` and `terraform_docs_without_aggregate_type_defaults` will insert/update documentation generated by [terraform-docs](https://github.com/segmentio/terraform-docs) between markers - `` and `` if they are present in `README.md`. Make sure that `terraform-docs` is installed. + +1. It is possible to pass additional arguments to shell scripts when using `terraform_docs` and `terraform_docs_without_aggregate_type_defaults`. Send pull-request with the new hook if there is something missing. + Enjoy the clean and documented code! + +## Authors + +This repository is managed by [Anton Babenko](https://github.com/antonbabenko) with help from [these awesome contributors](https://github.com/antonbabenko/pre-commit-terraform/graphs/contributors). + +## License + +MIT licensed. See LICENSE for full details. diff --git a/hooks.yaml b/hooks.yaml index 2fda38ba1..65436d109 100644 --- a/hooks.yaml +++ b/hooks.yaml @@ -10,6 +10,15 @@ name: Terraform docs description: Inserts input and output documentation into README.md (using terraform-docs). entry: terraform_docs.sh + args: [--args=--with-aggregate-type-defaults] + language: script + files: (\.tf)$ + exclude: \.terraform\/.*$ + +- id: terraform_docs_without_aggregate_type_defaults + name: Terraform docs (without aggregate type defaults) + description: Inserts input and output documentation into README.md (using terraform-docs). + entry: terraform_docs.sh language: script files: (\.tf)$ exclude: \.terraform\/.*$ From 9aa971c069da7ef66f0a30075e6b68f21f243059 Mon Sep 17 00:00:00 2001 From: rothandrew Date: Thu, 13 Dec 2018 22:16:01 -0500 Subject: [PATCH 011/214] Add new hook for running terraform-docs with replacing README.md from doc in main.tf --- .pre-commit-hooks.yaml | 8 ++++ README.md | 10 +++++ hooks.yaml | 40 ----------------- pre_commit_hooks/__init__.py | 0 pre_commit_hooks/terraform_docs_replace.py | 50 ++++++++++++++++++++++ setup.py | 34 +++++++++++++++ 6 files changed, 102 insertions(+), 40 deletions(-) delete mode 100644 hooks.yaml create mode 100644 pre_commit_hooks/__init__.py create mode 100644 pre_commit_hooks/terraform_docs_replace.py create mode 100644 setup.py diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 65436d109..64f2ad8ef 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -23,6 +23,14 @@ files: (\.tf)$ exclude: \.terraform\/.*$ +- id: terraform_docs_replace + name: Generate documentation for Terraform modules + language: python + entry: terraform_docs_replace + files: (\.tf)$ + exclude: \.terraform\/.*$ + description: Generates README.md files for Terraform modules + - id: terraform_validate_no_variables name: Terraform validate without variables description: Validates all Terraform configuration files without checking whether all required variables were set (basic check). diff --git a/README.md b/README.md index e3e7699ef..ab1d44df0 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ There are several [pre-commit](http://pre-commit.com/) hooks to keep Terraform c * `terraform_validate_with_variables` - Validates all Terraform configuration files and checks whether all required variables were specified. * `terraform_docs` - Inserts input and output documentation into `README.md`. Recommended. * `terraform_docs_without_aggregate_type_defaults` - Inserts input and output documentation into `README.md` without aggregate type defaults. +* `terraform_docs_replace` - Runs `terraform-docs` and pipes the output directly to README.md Check the [source file](https://github.com/antonbabenko/pre-commit-terraform/blob/master/.pre-commit-hooks.yaml) to know arguments used for each hook. @@ -61,6 +62,15 @@ Check the [source file](https://github.com/antonbabenko/pre-commit-terraform/blo 1. `terraform_docs` and `terraform_docs_without_aggregate_type_defaults` will insert/update documentation generated by [terraform-docs](https://github.com/segmentio/terraform-docs) between markers - `` and `` if they are present in `README.md`. Make sure that `terraform-docs` is installed. +1. `terraform_docs_replace` replaces the entire README.md rather than doing string replacement between markers. Put your additional documentation at the top of your `main.tf` for it to be pulled in. + + 1. Example: + ```yaml + hooks: + - id: terraform_docs_replace + args: ['--with-aggregate-type-defaults', '--sort-inputs-by-required'] + ``` + 1. It is possible to pass additional arguments to shell scripts when using `terraform_docs` and `terraform_docs_without_aggregate_type_defaults`. Send pull-request with the new hook if there is something missing. Enjoy the clean and documented code! diff --git a/hooks.yaml b/hooks.yaml deleted file mode 100644 index 65436d109..000000000 --- a/hooks.yaml +++ /dev/null @@ -1,40 +0,0 @@ -- id: terraform_fmt - name: Terraform fmt - description: Rewrites all Terraform configuration files to a canonical format. - entry: terraform_fmt.sh - language: script - files: (\.tf|\.tfvars)$ - exclude: \.terraform\/.*$ - -- id: terraform_docs - name: Terraform docs - description: Inserts input and output documentation into README.md (using terraform-docs). - entry: terraform_docs.sh - args: [--args=--with-aggregate-type-defaults] - language: script - files: (\.tf)$ - exclude: \.terraform\/.*$ - -- id: terraform_docs_without_aggregate_type_defaults - name: Terraform docs (without aggregate type defaults) - description: Inserts input and output documentation into README.md (using terraform-docs). - entry: terraform_docs.sh - language: script - files: (\.tf)$ - exclude: \.terraform\/.*$ - -- id: terraform_validate_no_variables - name: Terraform validate without variables - description: Validates all Terraform configuration files without checking whether all required variables were set (basic check). - entry: terraform_validate_no_variables.sh - language: script - files: (\.tf|\.tfvars)$ - exclude: \.terraform\/.*$ - -- id: terraform_validate_with_variables - name: Terraform validate with variables - description: Validates all Terraform configuration files and checks whether all required variables were specified. - entry: terraform_validate_with_variables.sh - language: script - files: (\.tf|\.tfvars)$ - exclude: \.terraform\/.*$ diff --git a/pre_commit_hooks/__init__.py b/pre_commit_hooks/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/pre_commit_hooks/terraform_docs_replace.py b/pre_commit_hooks/terraform_docs_replace.py new file mode 100644 index 000000000..31fbd1acd --- /dev/null +++ b/pre_commit_hooks/terraform_docs_replace.py @@ -0,0 +1,50 @@ +import argparse +import os +import subprocess +import sys + + +def main(argv=None): + parser = argparse.ArgumentParser( + description="""Run terraform-docs on a set of files. Follows the standard convention of + pulling the documentation from main.tf in order to replace the entire + README.md file each time.""" + ) + parser.add_argument( + '--sort-inputs-by-required', dest='sort', action='store_true', + ) + parser.add_argument( + '--with-aggregate-type-defaults', dest='aggregate', action='store_true', + ) + parser.add_argument('filenames', nargs='*', help='Filenames to check.') + args = parser.parse_args(argv) + + dirs = [] + for filename in args.filenames: + if os.path.realpath(filename) not in dirs: + dirs.append(os.path.dirname(filename)) + + retval = 0 + + for dir in dirs: + try: + procArgs = [] + procArgs.append('terraform-docs') + if args.sort: + procArgs.append('--sort-inputs-by-required') + if args.aggregate: + procArgs.append('--with-aggregate-type-defaults') + procArgs.append('md') + procArgs.append(dir) + procArgs.append("| sed -e '$ d' -e 'N;/^\\n$/D;P;D'") + procArgs.append('>') + procArgs.append('{}/README.md'.format(dir)) + subprocess.check_call(" ".join(procArgs), shell=True) + except subprocess.CalledProcessError as e: + print(e) + retval = 1 + return retval + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/setup.py b/setup.py new file mode 100644 index 000000000..0b2d6e378 --- /dev/null +++ b/setup.py @@ -0,0 +1,34 @@ +from setuptools import find_packages +from setuptools import setup + + +setup( + name='pre-commit-terraform', + description='Pre-commit hooks for Terraform', + url='https://github.com/antonbabenko/pre-commit-terraform', + version_format='{tag}+{gitsha}', + + author='Andrew Roth', + author_email='roth.andy@gmail.com', + + classifiers=[ + 'License :: OSI Approved :: MIT License', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: Implementation :: CPython', + 'Programming Language :: Python :: Implementation :: PyPy', + ], + + packages=find_packages(exclude=('tests*', 'testing*')), + install_requires=[ + 'setuptools-git-version', + ], + entry_points={ + 'console_scripts': [ + 'terraform_docs_replace = pre_commit_hooks.terraform_docs_replace:main', + ], + }, +) From debe93a82be27d9dab99858372a33cdde196a320 Mon Sep 17 00:00:00 2001 From: rothandrew Date: Fri, 14 Dec 2018 09:23:54 -0500 Subject: [PATCH 012/214] Address requested changes --- .pre-commit-hooks.yaml | 4 ++-- setup.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 64f2ad8ef..e83763601 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -24,12 +24,12 @@ exclude: \.terraform\/.*$ - id: terraform_docs_replace - name: Generate documentation for Terraform modules + name: Terraform docs (overwrite README.md) language: python entry: terraform_docs_replace files: (\.tf)$ exclude: \.terraform\/.*$ - description: Generates README.md files for Terraform modules + description: Overwrite content of README.md with terraform-docs - id: terraform_validate_no_variables name: Terraform validate without variables diff --git a/setup.py b/setup.py index 0b2d6e378..44ae516ea 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ setup( name='pre-commit-terraform', - description='Pre-commit hooks for Terraform', + description='Pre-commit hooks for terraform_docs_replace, url='https://github.com/antonbabenko/pre-commit-terraform', version_format='{tag}+{gitsha}', From cbd26b20c7dcae9b15ccecc7bf76a5e1c0ffdc81 Mon Sep 17 00:00:00 2001 From: rothandrew Date: Fri, 14 Dec 2018 10:45:59 -0500 Subject: [PATCH 013/214] Add `--dest` argument --- README.md | 4 ++-- pre_commit_hooks/terraform_docs_replace.py | 9 +++++++-- setup.py | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ab1d44df0..84ed48114 100644 --- a/README.md +++ b/README.md @@ -62,13 +62,13 @@ Check the [source file](https://github.com/antonbabenko/pre-commit-terraform/blo 1. `terraform_docs` and `terraform_docs_without_aggregate_type_defaults` will insert/update documentation generated by [terraform-docs](https://github.com/segmentio/terraform-docs) between markers - `` and `` if they are present in `README.md`. Make sure that `terraform-docs` is installed. -1. `terraform_docs_replace` replaces the entire README.md rather than doing string replacement between markers. Put your additional documentation at the top of your `main.tf` for it to be pulled in. +1. `terraform_docs_replace` replaces the entire README.md rather than doing string replacement between markers. Put your additional documentation at the top of your `main.tf` for it to be pulled in. The optional `--dest` argument lets your change the name of the file that gets created/modified 1. Example: ```yaml hooks: - id: terraform_docs_replace - args: ['--with-aggregate-type-defaults', '--sort-inputs-by-required'] + args: ['--with-aggregate-type-defaults', '--sort-inputs-by-required', '--dest=TEST.md'] ``` 1. It is possible to pass additional arguments to shell scripts when using `terraform_docs` and `terraform_docs_without_aggregate_type_defaults`. Send pull-request with the new hook if there is something missing. diff --git a/pre_commit_hooks/terraform_docs_replace.py b/pre_commit_hooks/terraform_docs_replace.py index 31fbd1acd..7e3bc2fb8 100644 --- a/pre_commit_hooks/terraform_docs_replace.py +++ b/pre_commit_hooks/terraform_docs_replace.py @@ -10,6 +10,9 @@ def main(argv=None): pulling the documentation from main.tf in order to replace the entire README.md file each time.""" ) + parser.add_argument( + '--dest', dest='dest', default='README.md', + ) parser.add_argument( '--sort-inputs-by-required', dest='sort', action='store_true', ) @@ -21,7 +24,9 @@ def main(argv=None): dirs = [] for filename in args.filenames: - if os.path.realpath(filename) not in dirs: + if (os.path.realpath(filename) not in dirs and \ + len(os.path.realpath(filename).strip()) > 0 and \ + (filename.endswith(".tf") or filename.endswith(".tfvars"))): dirs.append(os.path.dirname(filename)) retval = 0 @@ -38,7 +43,7 @@ def main(argv=None): procArgs.append(dir) procArgs.append("| sed -e '$ d' -e 'N;/^\\n$/D;P;D'") procArgs.append('>') - procArgs.append('{}/README.md'.format(dir)) + procArgs.append("./{dir}/{dest}".format(dir=dir,dest=args.dest)) subprocess.check_call(" ".join(procArgs), shell=True) except subprocess.CalledProcessError as e: print(e) diff --git a/setup.py b/setup.py index 44ae516ea..246de8c2b 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ setup( name='pre-commit-terraform', - description='Pre-commit hooks for terraform_docs_replace, + description='Pre-commit hooks for terraform_docs_replace', url='https://github.com/antonbabenko/pre-commit-terraform', version_format='{tag}+{gitsha}', From d3fe87daeac07704ae27e02e87df719dd7c9ac9f Mon Sep 17 00:00:00 2001 From: rothandrew Date: Fri, 14 Dec 2018 16:16:42 -0500 Subject: [PATCH 014/214] Address requested changes --- README.md | 8 +++++++- setup.py | 3 +-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 84ed48114..1bceb9517 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ Check the [source file](https://github.com/antonbabenko/pre-commit-terraform/blo 1. `terraform_docs` and `terraform_docs_without_aggregate_type_defaults` will insert/update documentation generated by [terraform-docs](https://github.com/segmentio/terraform-docs) between markers - `` and `` if they are present in `README.md`. Make sure that `terraform-docs` is installed. -1. `terraform_docs_replace` replaces the entire README.md rather than doing string replacement between markers. Put your additional documentation at the top of your `main.tf` for it to be pulled in. The optional `--dest` argument lets your change the name of the file that gets created/modified +1. `terraform_docs_replace` replaces the entire README.md rather than doing string replacement between markers. Put your additional documentation at the top of your `main.tf` for it to be pulled in. The optional `--dest` argument lets your change the name of the file that gets created/modified. 1. Example: ```yaml @@ -73,6 +73,12 @@ Check the [source file](https://github.com/antonbabenko/pre-commit-terraform/blo 1. It is possible to pass additional arguments to shell scripts when using `terraform_docs` and `terraform_docs_without_aggregate_type_defaults`. Send pull-request with the new hook if there is something missing. +## Notes for developers + +1. Python hooks are supported now too. All you have to do is: + 1. add a line to the `console_sripts` array in `entry_points` in `setup.py` + 1. Put your python script in the `pre_commit_hooks` folder + Enjoy the clean and documented code! ## Authors diff --git a/setup.py b/setup.py index 246de8c2b..4c2b47668 100644 --- a/setup.py +++ b/setup.py @@ -8,8 +8,7 @@ url='https://github.com/antonbabenko/pre-commit-terraform', version_format='{tag}+{gitsha}', - author='Andrew Roth', - author_email='roth.andy@gmail.com', + author='Contributors', classifiers=[ 'License :: OSI Approved :: MIT License', From fe3ba02d25e3c7baa7f0fd3f97aaf3f2c3db62c6 Mon Sep 17 00:00:00 2001 From: rothandrew Date: Fri, 14 Dec 2018 16:19:54 -0500 Subject: [PATCH 015/214] fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1bceb9517..5e114729d 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ Check the [source file](https://github.com/antonbabenko/pre-commit-terraform/blo 1. `terraform_docs` and `terraform_docs_without_aggregate_type_defaults` will insert/update documentation generated by [terraform-docs](https://github.com/segmentio/terraform-docs) between markers - `` and `` if they are present in `README.md`. Make sure that `terraform-docs` is installed. -1. `terraform_docs_replace` replaces the entire README.md rather than doing string replacement between markers. Put your additional documentation at the top of your `main.tf` for it to be pulled in. The optional `--dest` argument lets your change the name of the file that gets created/modified. +1. `terraform_docs_replace` replaces the entire README.md rather than doing string replacement between markers. Put your additional documentation at the top of your `main.tf` for it to be pulled in. The optional `--dest` argument lets you change the name of the file that gets created/modified. 1. Example: ```yaml From 15c9f394d32708ad3d2735af9c73cb03a6bad1af Mon Sep 17 00:00:00 2001 From: rothandrew Date: Fri, 14 Dec 2018 17:03:14 -0500 Subject: [PATCH 016/214] Fix bug not letting terraform_docs_replace work in the root directory of a repo --- pre_commit_hooks/terraform_docs_replace.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pre_commit_hooks/terraform_docs_replace.py b/pre_commit_hooks/terraform_docs_replace.py index 7e3bc2fb8..05fec622e 100644 --- a/pre_commit_hooks/terraform_docs_replace.py +++ b/pre_commit_hooks/terraform_docs_replace.py @@ -25,7 +25,6 @@ def main(argv=None): dirs = [] for filename in args.filenames: if (os.path.realpath(filename) not in dirs and \ - len(os.path.realpath(filename).strip()) > 0 and \ (filename.endswith(".tf") or filename.endswith(".tfvars"))): dirs.append(os.path.dirname(filename)) @@ -40,7 +39,7 @@ def main(argv=None): if args.aggregate: procArgs.append('--with-aggregate-type-defaults') procArgs.append('md') - procArgs.append(dir) + procArgs.append("./{dir}".format(dir=dir)) procArgs.append("| sed -e '$ d' -e 'N;/^\\n$/D;P;D'") procArgs.append('>') procArgs.append("./{dir}/{dest}".format(dir=dir,dest=args.dest)) From 57d924d5d4621d6e2635c8daea931b657f66f050 Mon Sep 17 00:00:00 2001 From: Chris Gilmer Date: Fri, 8 Feb 2019 15:24:06 -0800 Subject: [PATCH 017/214] Require terraform-docs runs in serial to avoid pre-commit doing parallel operations on similar file paths --- .pre-commit-hooks.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index e83763601..afb5d6066 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -9,6 +9,7 @@ - id: terraform_docs name: Terraform docs description: Inserts input and output documentation into README.md (using terraform-docs). + require_serial: true entry: terraform_docs.sh args: [--args=--with-aggregate-type-defaults] language: script @@ -18,6 +19,7 @@ - id: terraform_docs_without_aggregate_type_defaults name: Terraform docs (without aggregate type defaults) description: Inserts input and output documentation into README.md (using terraform-docs). + require_serial: true entry: terraform_docs.sh language: script files: (\.tf)$ @@ -25,11 +27,12 @@ - id: terraform_docs_replace name: Terraform docs (overwrite README.md) - language: python + description: Overwrite content of README.md with terraform-docs + require_serial: true entry: terraform_docs_replace + language: python files: (\.tf)$ exclude: \.terraform\/.*$ - description: Overwrite content of README.md with terraform-docs - id: terraform_validate_no_variables name: Terraform validate without variables From bc0e68bfd3ec0c777fcb511baf94af323d40c620 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Mon, 18 Feb 2019 18:52:10 +0100 Subject: [PATCH 018/214] Added chglog (hi @robinbowes :)) --- .chglog/CHANGELOG.tpl.md | 51 ++++++++++++++++++++++++++++++++++++++++ .chglog/config.yml | 10 ++++++++ CHANGELOG.md | 0 Makefile | 7 ++++++ 4 files changed, 68 insertions(+) create mode 100644 .chglog/CHANGELOG.tpl.md create mode 100644 .chglog/config.yml create mode 100644 CHANGELOG.md create mode 100644 Makefile diff --git a/.chglog/CHANGELOG.tpl.md b/.chglog/CHANGELOG.tpl.md new file mode 100644 index 000000000..a58b0e452 --- /dev/null +++ b/.chglog/CHANGELOG.tpl.md @@ -0,0 +1,51 @@ +{{ if .Versions -}} + +## [Unreleased] +{{ if .Unreleased.CommitGroups -}} +{{ range .Unreleased.CommitGroups -}} +### {{ .Title }} +{{ range .Commits -}} +- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} +{{ end }} +{{ end -}} +{{ else }} +{{ range .Unreleased.Commits -}} +- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} +{{ end }} +{{ end -}} +{{ end -}} + +{{ range .Versions }} + +## {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }} +{{ if .CommitGroups -}} +{{ range .CommitGroups -}} +### {{ .Title }} +{{ range .Commits -}} +- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} +{{ end }} +{{ end -}} +{{ else }} +{{ range .Commits -}} +- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} +{{ end }} +{{ end -}} + +{{- if .NoteGroups -}} +{{ range .NoteGroups -}} +### {{ .Title }} +{{ range .Notes }} +{{ .Body }} +{{ end }} +{{ end -}} +{{ end -}} +{{ end -}} + +{{- if .Versions }} +[Unreleased]: {{ .Info.RepositoryURL }}/compare/{{ $latest := index .Versions 0 }}{{ $latest.Tag.Name }}...HEAD +{{ range .Versions -}} +{{ if .Tag.Previous -}} +[{{ .Tag.Name }}]: {{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }} +{{ end -}} +{{ end -}} +{{ end -}} diff --git a/.chglog/config.yml b/.chglog/config.yml new file mode 100644 index 000000000..3529dbcd6 --- /dev/null +++ b/.chglog/config.yml @@ -0,0 +1,10 @@ +style: github +template: CHANGELOG.tpl.md +info: + title: CHANGELOG + repository_url: https://github.com/antonbabenko/pre-commit-terraform +options: + header: + pattern: "^(.*)$" + pattern_maps: + - Subject diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..e69de29bb diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..558dac5a6 --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +.PHONY: changelog release + +changelog: + git-chglog -o CHANGELOG.md --next-tag `semtag final -s minor -o` + +release: + semtag final -s minor From 66214dcc4b092a81e0146b2e66745627386b90ee Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Mon, 18 Feb 2019 18:52:46 +0100 Subject: [PATCH 019/214] Added CHANGELOG.md --- CHANGELOG.md | 141 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..eb4b86403 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1,141 @@ + +## [Unreleased] + + + + +## [v1.9.0] - 2019-02-18 + +- Added chglog (hi [@robinbowes](https://github.com/robinbowes) :)) +- Merge pull request [#33](https://github.com/antonbabenko/pre-commit-terraform/issues/33) from chrisgilmerproj/run_terraform_docs_in_serial +- Require terraform-docs runs in serial to avoid pre-commit doing parallel operations on similar file paths + + + +## [v1.8.1] - 2018-12-15 + +- Merge pull request [#30](https://github.com/antonbabenko/pre-commit-terraform/issues/30) from RothAndrew/feature/fix_issue_29 +- Fix bug not letting terraform_docs_replace work in the root directory of a repo + + + +## [v1.8.0] - 2018-12-14 + +- Merge pull request [#27](https://github.com/antonbabenko/pre-commit-terraform/issues/27) from RothAndrew/feature/new_hook +- fix typo +- Address requested changes +- Add `--dest` argument +- Address requested changes +- Add new hook for running terraform-docs with replacing README.md from doc in main.tf + + + +## [v1.7.4] - 2018-12-11 + +- Merge remote-tracking branch 'origin/master' into pr25 +- Added followup after [#25](https://github.com/antonbabenko/pre-commit-terraform/issues/25) +- Merge pull request [#25](https://github.com/antonbabenko/pre-commit-terraform/issues/25) from getcloudnative/feat-pass-terraform-docs-opts +- Add feature to pass options to terraform-docs. +- Added license file (fixed [#21](https://github.com/antonbabenko/pre-commit-terraform/issues/21)) + + + +## [v1.7.3] - 2018-05-24 + +- Updated README +- Only run validate if .tf files exist in the directory. ([#20](https://github.com/antonbabenko/pre-commit-terraform/issues/20)) + + + +## [v1.7.2] - 2018-05-20 + +- Replace terraform_docs use of GNU sed with perl ([#15](https://github.com/antonbabenko/pre-commit-terraform/issues/15)) +- Fixes use of md5 for tempfile name ([#16](https://github.com/antonbabenko/pre-commit-terraform/issues/16)) + + + +## [v1.7.1] - 2018-05-16 + +- Run terraform_docs only if README.md is present +- Run terraform_docs only if README.md is present + + + +## [v1.7.0] - 2018-05-16 + +- Added terraform-docs integration ([#13](https://github.com/antonbabenko/pre-commit-terraform/issues/13)) + + + +## [v1.6.0] - 2018-04-21 + +- Allow to have spaces in directories ([#11](https://github.com/antonbabenko/pre-commit-terraform/issues/11)) + + + +## [v1.5.0] - 2018-03-06 + +- Bump new version +- Format tfvars files explicitely, because terraform fmt ignores them ([#9](https://github.com/antonbabenko/pre-commit-terraform/issues/9)) + + + +## [v1.4.0] - 2018-01-24 + +- Updated readme +- Show failed path +- Show failed path +- Show failed path +- Updated scripts +- Added scripts to validate terraform files + + + +## [v1.3.0] - 2018-01-15 + +- Added badges +- Added formatting for tfvars (fixes [#4](https://github.com/antonbabenko/pre-commit-terraform/issues/4)) ([#6](https://github.com/antonbabenko/pre-commit-terraform/issues/6)) +- Merge pull request [#5](https://github.com/antonbabenko/pre-commit-terraform/issues/5) from schneems/schneems/codetriage-badge +- [ci skip] Get more Open Source Helpers + + + +## [v1.2.0] - 2017-06-08 + +- Renamed shell script file to the correct one +- Updated .pre-commit-hooks.yaml +- Updated sha in README +- Merge pull request [#3](https://github.com/antonbabenko/pre-commit-terraform/issues/3) from pecigonzalo/master +- Exclude .terraform even on subfolders + + + +## [v1.1.0] - 2017-02-04 + +- Copied to .pre-commit-hooks.yaml for compatibility (closes [#1](https://github.com/antonbabenko/pre-commit-terraform/issues/1)) + + + +## v1.0.0 - 2016-09-27 + +- Updated README +- Ready, probably :) +- Initial commit +- Initial commit + + +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.9.0...HEAD +[v1.9.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.8.1...v1.9.0 +[v1.8.1]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.8.0...v1.8.1 +[v1.8.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.7.4...v1.8.0 +[v1.7.4]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.7.3...v1.7.4 +[v1.7.3]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.7.2...v1.7.3 +[v1.7.2]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.7.1...v1.7.2 +[v1.7.1]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.7.0...v1.7.1 +[v1.7.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.6.0...v1.7.0 +[v1.6.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.5.0...v1.6.0 +[v1.5.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.4.0...v1.5.0 +[v1.4.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.3.0...v1.4.0 +[v1.3.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.2.0...v1.3.0 +[v1.2.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.1.0...v1.2.0 +[v1.1.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.0.0...v1.1.0 From beb4a677531027e2991cade71b9567995939845a Mon Sep 17 00:00:00 2001 From: Josiah Halme Date: Thu, 21 Feb 2019 19:38:50 +1100 Subject: [PATCH 020/214] Add exit code for 'terraform validate' so pre-commit check fails (#34) --- terraform_validate_with_variables.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/terraform_validate_with_variables.sh b/terraform_validate_with_variables.sh index 706443ade..01d5536f8 100755 --- a/terraform_validate_with_variables.sh +++ b/terraform_validate_with_variables.sh @@ -3,6 +3,7 @@ set -e declare -a paths index=0 +error=0 for file_with_path in "$@"; do file_with_path="${file_with_path// /__REPLACED__SPACE__}" @@ -17,6 +18,7 @@ for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do pushd "$path_uniq" > /dev/null if [[ -n "$(find . -maxdepth 1 -name '*.tf' -print -quit)" ]] ; then if ! terraform validate -check-variables=true ; then + error=1 echo echo "Failed path: $path_uniq" echo "================================" @@ -24,3 +26,7 @@ for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do fi popd > /dev/null done + +if [[ -n "${error}" ]] ; then + exit 1 +fi From 2c842d94615268be20391c248b91b070e60c5912 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Thu, 21 Feb 2019 09:43:31 +0100 Subject: [PATCH 021/214] Bump new version --- CHANGELOG.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb4b86403..2fca9c7e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,16 @@ + +## [v1.10.0] - 2019-02-21 + +- Add exit code for 'terraform validate' so pre-commit check fails ([#34](https://github.com/antonbabenko/pre-commit-terraform/issues/34)) + + ## [v1.9.0] - 2019-02-18 +- Added CHANGELOG.md - Added chglog (hi [@robinbowes](https://github.com/robinbowes) :)) - Merge pull request [#33](https://github.com/antonbabenko/pre-commit-terraform/issues/33) from chrisgilmerproj/run_terraform_docs_in_serial - Require terraform-docs runs in serial to avoid pre-commit doing parallel operations on similar file paths @@ -124,7 +131,8 @@ - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.9.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.10.0...HEAD +[v1.10.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.9.0...v1.10.0 [v1.9.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.8.1...v1.9.0 [v1.8.1]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.8.0...v1.8.1 [v1.8.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.7.4...v1.8.0 From a3771b5119231be9677934b88ee47203e9cb5a90 Mon Sep 17 00:00:00 2001 From: Tyler Christiansen Date: Fri, 1 Mar 2019 00:48:50 -0800 Subject: [PATCH 022/214] fix check for errors at the end (#35) --- terraform_validate_with_variables.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform_validate_with_variables.sh b/terraform_validate_with_variables.sh index 01d5536f8..355a91372 100755 --- a/terraform_validate_with_variables.sh +++ b/terraform_validate_with_variables.sh @@ -27,6 +27,6 @@ for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do popd > /dev/null done -if [[ -n "${error}" ]] ; then +if [[ "${error}" -ne 0 ]] ; then exit 1 fi From 7eb9ca3f2f70302227d1315a66a5a868562c02b7 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Fri, 1 Mar 2019 09:49:34 +0100 Subject: [PATCH 023/214] Updated changelog --- CHANGELOG.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fca9c7e4..40cc5bac8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,16 @@ + +## [v1.11.0] - 2019-03-01 + +- fix check for errors at the end ([#35](https://github.com/antonbabenko/pre-commit-terraform/issues/35)) + + ## [v1.10.0] - 2019-02-21 +- Bump new version - Add exit code for 'terraform validate' so pre-commit check fails ([#34](https://github.com/antonbabenko/pre-commit-terraform/issues/34)) @@ -131,7 +138,8 @@ - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.10.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.11.0...HEAD +[v1.11.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.10.0...v1.11.0 [v1.10.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.9.0...v1.10.0 [v1.9.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.8.1...v1.9.0 [v1.8.1]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.8.0...v1.8.1 From 249c02bb64e9f8a9b08a11c723fd78590cdccea2 Mon Sep 17 00:00:00 2001 From: Guido Dobboletta Date: Tue, 5 Mar 2019 02:16:55 -0600 Subject: [PATCH 024/214] Update README.md (#36) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5e114729d..d26f97f20 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Step into the repository you want to have the pre-commit hooks installed and run ```bash cat < .pre-commit-config.yaml - repo: git://github.com/antonbabenko/pre-commit-terraform - rev: v1.7.4 + rev: v1.11.0 hooks: - id: terraform_fmt - id: terraform_docs From 418a5ec782f8ed37d804a539ef1b2d3c14bee8bf Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Sat, 6 Apr 2019 21:19:39 +0200 Subject: [PATCH 025/214] Fixed broken "maintained badge" --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d26f97f20..08749368b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Collection of git hooks for Terraform to be used with [pre-commit framework](http://pre-commit.com/) -[![Github tag](https://img.shields.io/github/tag/antonbabenko/pre-commit-terraform.svg)](https://github.com/antonbabenko/pre-commit-terraform/releases) ![](https://img.shields.io/maintenance/yes/2018.svg) [![Help Contribute to Open Source](https://www.codetriage.com/antonbabenko/pre-commit-terraform/badges/users.svg)](https://www.codetriage.com/antonbabenko/pre-commit-terraform) +[![Github tag](https://img.shields.io/github/tag/antonbabenko/pre-commit-terraform.svg)](https://github.com/antonbabenko/pre-commit-terraform/releases) ![](https://img.shields.io/maintenance/yes/2019.svg) [![Help Contribute to Open Source](https://www.codetriage.com/antonbabenko/pre-commit-terraform/badges/users.svg)](https://www.codetriage.com/antonbabenko/pre-commit-terraform) ## How to install From 5725b11b558a03eb787623d53a0c7c157cb75f3b Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Mon, 27 May 2019 09:33:32 -0700 Subject: [PATCH 026/214] Added note about incompatibility of terraform-docs with Terraform 0.12 (#41) --- .pre-commit-config.yaml | 2 +- README.md | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1c444a95a..f80cfc805 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: git://github.com/pre-commit/pre-commit-hooks - rev: v2.0.0 + rev: v2.2.3 hooks: - id: check-yaml - id: end-of-file-fixer diff --git a/README.md b/README.md index 08749368b..fac045ae5 100644 --- a/README.md +++ b/README.md @@ -21,10 +21,10 @@ Step into the repository you want to have the pre-commit hooks installed and run ```bash cat < .pre-commit-config.yaml - repo: git://github.com/antonbabenko/pre-commit-terraform - rev: v1.11.0 + rev: v1.12.0 hooks: - id: terraform_fmt - - id: terraform_docs + # - id: terraform_docs # terraform-docs is not working with Terraform 0.12 yet (read note in README) EOF ``` @@ -73,6 +73,8 @@ Check the [source file](https://github.com/antonbabenko/pre-commit-terraform/blo 1. It is possible to pass additional arguments to shell scripts when using `terraform_docs` and `terraform_docs_without_aggregate_type_defaults`. Send pull-request with the new hook if there is something missing. +1. `terraform-docs` is not working with Terraform 0.12 yet (see [issue #62](https://github.com/segmentio/terraform-docs/issues/62)), so remember to disable `terraform_docs`, `terraform_docs_replace` and `terraform_docs_without_aggregate_type_defaults` hooks in your `.pre-commit-config.yaml` + ## Notes for developers 1. Python hooks are supported now too. All you have to do is: From dbf91086f31cef3fdf3652d62eae57ffce943568 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Mon, 27 May 2019 09:34:00 -0700 Subject: [PATCH 027/214] Updated CHANGELOG --- CHANGELOG.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40cc5bac8..ad55efa94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,18 @@ + +## [v1.12.0] - 2019-05-27 + +- Added note about incompatibility of terraform-docs with Terraform 0.12 ([#41](https://github.com/antonbabenko/pre-commit-terraform/issues/41)) +- Fixed broken "maintained badge" +- Update README.md ([#36](https://github.com/antonbabenko/pre-commit-terraform/issues/36)) + + ## [v1.11.0] - 2019-03-01 +- Updated changelog - fix check for errors at the end ([#35](https://github.com/antonbabenko/pre-commit-terraform/issues/35)) @@ -138,7 +147,8 @@ - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.11.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.12.0...HEAD +[v1.12.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.11.0...v1.12.0 [v1.11.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.10.0...v1.11.0 [v1.10.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.9.0...v1.10.0 [v1.9.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.8.1...v1.9.0 From 8cddce38b0953e48c32909ba75408f50c6075e07 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Mon, 17 Jun 2019 12:47:06 +0200 Subject: [PATCH 028/214] Added support for terraform_docs for Terraform 0.12 (#45) --- README.md | 10 ++-- terraform_docs.sh | 113 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 115 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index fac045ae5..b39066b79 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,10 @@ ### Step 1 -On MacOSX install the pre-commit package +On MacOSX install the `pre-commit` and `awk` (required for Terraform 0.12) package ```bash -brew install pre-commit +brew install pre-commit awk ``` For other operating systems check the [official documentation](http://pre-commit.com/#install) @@ -21,10 +21,10 @@ Step into the repository you want to have the pre-commit hooks installed and run ```bash cat < .pre-commit-config.yaml - repo: git://github.com/antonbabenko/pre-commit-terraform - rev: v1.12.0 + rev: v1.13.0 hooks: - id: terraform_fmt - # - id: terraform_docs # terraform-docs is not working with Terraform 0.12 yet (read note in README) + - id: terraform_docs EOF ``` @@ -73,7 +73,7 @@ Check the [source file](https://github.com/antonbabenko/pre-commit-terraform/blo 1. It is possible to pass additional arguments to shell scripts when using `terraform_docs` and `terraform_docs_without_aggregate_type_defaults`. Send pull-request with the new hook if there is something missing. -1. `terraform-docs` is not working with Terraform 0.12 yet (see [issue #62](https://github.com/segmentio/terraform-docs/issues/62)), so remember to disable `terraform_docs`, `terraform_docs_replace` and `terraform_docs_without_aggregate_type_defaults` hooks in your `.pre-commit-config.yaml` +1. `terraform-docs` works with Terraform 0.12 but support is hackish (it requires `awk` to be installed) and may contain bugs. You can follow the native support of Terraform 0.12 in `terraform-docs` in [issue #62](https://github.com/segmentio/terraform-docs/issues/62). ## Notes for developers diff --git a/terraform_docs.sh b/terraform_docs.sh index 1bd79627a..191a4a2bd 100755 --- a/terraform_docs.sh +++ b/terraform_docs.sh @@ -25,12 +25,25 @@ main() { esac done - terraform_docs "$args" "$files" + local hack_terraform_docs=$(terraform version | head -1 | grep -c 0.12) + + if [[ "$hack_terraform_docs" == "1" ]]; then + which awk 2>&1 >/dev/null || ( echo "awk is required for terraform-docs hack to work with Terraform 0.12"; exit 1) + + TMP_AWK_FILE="$(mktemp --tmpdir terraform-docs-XXXXXXXXXX.awk)" + terraform_docs_awk $TMP_AWK_FILE + terraform_docs "$TMP_AWK_FILE" "$args" "$files" + rm -f "$TMP_AWK_FILE" + else + terraform_docs "0" "$args" "$files" + fi + } terraform_docs() { - readonly args="$1" - readonly files="$2" + readonly terraform_docs_awk_file="$1" + readonly args="$2" + readonly files="$3" declare -a paths declare -a tfvars_files @@ -62,7 +75,14 @@ terraform_docs() { continue fi + if [[ "$terraform_docs_awk_file" == "0" ]]; then terraform-docs $args md ./ > "$tmp_file" + else + TMP_FILE="$(mktemp --tmpdir terraform-docs-XXXXXXXXXX.tf)" + awk -f "$terraform_docs_awk_file" ./*.tf > "$TMP_FILE" + terraform-docs $args md "$TMP_FILE" > "$tmp_file" + rm -f "$TMP_FILE" + fi # 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" @@ -76,6 +96,93 @@ terraform_docs() { done } +terraform_docs_awk() { + readonly 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/segmentio/terraform-docs/ +# https://github.com/segmentio/terraform-docs/issues/62 + +{ + if ( /\{/ ) { + braceCnt++ + } + + if ( /\}/ ) { + braceCnt-- + } + + # [START] variable or output block started + if ($0 ~ /(variable|output) "(.*?)"/) { + # [CLOSE] "default" block + if (blockDefCnt > 0) { + blockDefCnt = 0 + } + blockCnt++ + print $0 + } + + # [START] multiline default statement started + if (blockCnt > 0) { + if ($1 == "default") { + print $0 + if ($NF ~ /[\[\(\{]/) { + blockDefCnt++ + blockDefStart=1 + } + } + } + + # [PRINT] single line "description" + if (blockDefCnt == 0) { + if ($1 == "description") { + # [CLOSE] "default" block + if (blockDefCnt > 0) { + blockDefCnt = 0 + } + print $0 + } + } + + # [PRINT] single line "type" + if (blockCnt > 0) { + if ($1 == "type" ) { + # [CLOSE] "default" block + if (blockDefCnt > 0) { + blockDefCnt = 0 + } + type=$3 + if (type ~ "object") { + print " type = \"object\"" + } else { + print " type = \"" $3 "\"" + } + } + } + + # [CLOSE] variable/output block + if (blockCnt > 0) { + if (braceCnt == 0 && blockCnt > 0) { + blockCnt-- + print $0 + } + } + + # [PRINT] Multiline "default" statement + if (blockCnt > 0 && blockDefCnt > 0) { + if (blockDefStart == 1) { + blockDefStart = 0 + } else { + print $0 + } + } +} +EOF + +} + getopt() { # pure-getopt, a drop-in replacement for GNU getopt in pure Bash. # version 1.4.3 From 9300d0f1942bf03f6a41225a445368014b2f5f62 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Mon, 17 Jun 2019 12:47:58 +0200 Subject: [PATCH 029/214] Updated CHANGELOG --- CHANGELOG.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad55efa94..bc5a04e33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,16 @@ + +## [v1.13.0] - 2019-06-17 + +- Added support for terraform_docs for Terraform 0.12 ([#45](https://github.com/antonbabenko/pre-commit-terraform/issues/45)) + + ## [v1.12.0] - 2019-05-27 +- Updated CHANGELOG - Added note about incompatibility of terraform-docs with Terraform 0.12 ([#41](https://github.com/antonbabenko/pre-commit-terraform/issues/41)) - Fixed broken "maintained badge" - Update README.md ([#36](https://github.com/antonbabenko/pre-commit-terraform/issues/36)) @@ -147,7 +154,8 @@ - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.12.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.13.0...HEAD +[v1.13.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.12.0...v1.13.0 [v1.12.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.11.0...v1.12.0 [v1.11.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.10.0...v1.11.0 [v1.10.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.9.0...v1.10.0 From 35e0356188b64a4c5af9a4e7200d936e514cba71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Szczepaniak?= Date: Mon, 17 Jun 2019 13:09:31 +0200 Subject: [PATCH 030/214] Upgraded to work with Terraform >= 0.12 (#44) --- .pre-commit-hooks.yaml | 16 +++--------- README.md | 5 +--- ...with_variables.sh => terraform_validate.sh | 4 +-- terraform_validate_no_variables.sh | 26 ------------------- 4 files changed, 6 insertions(+), 45 deletions(-) rename terraform_validate_with_variables.sh => terraform_validate.sh (84%) delete mode 100755 terraform_validate_no_variables.sh diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index afb5d6066..ee10f8228 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -27,25 +27,17 @@ - id: terraform_docs_replace name: Terraform docs (overwrite README.md) - description: Overwrite content of README.md with terraform-docs + description: Overwrite content of README.md with terraform-docs. require_serial: true entry: terraform_docs_replace language: python files: (\.tf)$ exclude: \.terraform\/.*$ -- id: terraform_validate_no_variables +- id: terraform_validate name: Terraform validate without variables - description: Validates all Terraform configuration files without checking whether all required variables were set (basic check). - entry: terraform_validate_no_variables.sh - language: script - files: (\.tf|\.tfvars)$ - exclude: \.terraform\/.*$ - -- id: terraform_validate_with_variables - name: Terraform validate with variables - description: Validates all Terraform configuration files and checks whether all required variables were specified. - entry: terraform_validate_with_variables.sh + description: Validates all Terraform configuration files. + entry: terraform_validate.sh language: script files: (\.tf|\.tfvars)$ exclude: \.terraform\/.*$ diff --git a/README.md b/README.md index b39066b79..370ac9eff 100644 --- a/README.md +++ b/README.md @@ -48,8 +48,7 @@ pre-commit run -a There are several [pre-commit](http://pre-commit.com/) hooks to keep Terraform configurations (both `*.tf` and `*.tfvars`) in a good shape: * `terraform_fmt` - Rewrites all Terraform configuration files to a canonical format. -* `terraform_validate_no_variables` - Validates all Terraform configuration files without checking whether all required variables were set. -* `terraform_validate_with_variables` - Validates all Terraform configuration files and checks whether all required variables were specified. +* `terraform_validate` - Validates all Terraform configuration files. * `terraform_docs` - Inserts input and output documentation into `README.md`. Recommended. * `terraform_docs_without_aggregate_type_defaults` - Inserts input and output documentation into `README.md` without aggregate type defaults. * `terraform_docs_replace` - Runs `terraform-docs` and pipes the output directly to README.md @@ -58,8 +57,6 @@ Check the [source file](https://github.com/antonbabenko/pre-commit-terraform/blo ## Notes about hooks -1. `terraform_validate_no_variables` and `terraform_validate_with_variables` will not work if variables are being set dynamically (eg, when using [Terragrunt](https://github.com/gruntwork-io/terragrunt)). Use `terragrunt validate` command instead. - 1. `terraform_docs` and `terraform_docs_without_aggregate_type_defaults` will insert/update documentation generated by [terraform-docs](https://github.com/segmentio/terraform-docs) between markers - `` and `` if they are present in `README.md`. Make sure that `terraform-docs` is installed. 1. `terraform_docs_replace` replaces the entire README.md rather than doing string replacement between markers. Put your additional documentation at the top of your `main.tf` for it to be pulled in. The optional `--dest` argument lets you change the name of the file that gets created/modified. diff --git a/terraform_validate_with_variables.sh b/terraform_validate.sh similarity index 84% rename from terraform_validate_with_variables.sh rename to terraform_validate.sh index 355a91372..6f7eccac2 100755 --- a/terraform_validate_with_variables.sh +++ b/terraform_validate.sh @@ -15,16 +15,14 @@ done for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do path_uniq="${path_uniq//__REPLACED__SPACE__/ }" - pushd "$path_uniq" > /dev/null if [[ -n "$(find . -maxdepth 1 -name '*.tf' -print -quit)" ]] ; then - if ! terraform validate -check-variables=true ; then + if ! terraform validate $path_uniq; then error=1 echo echo "Failed path: $path_uniq" echo "================================" fi fi - popd > /dev/null done if [[ "${error}" -ne 0 ]] ; then diff --git a/terraform_validate_no_variables.sh b/terraform_validate_no_variables.sh deleted file mode 100755 index 2e190448a..000000000 --- a/terraform_validate_no_variables.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env bash -set -e - -declare -a paths -index=0 - -for file_with_path in "$@"; do - file_with_path="${file_with_path// /__REPLACED__SPACE__}" - - paths[index]=$(dirname "$file_with_path") - (( "index+=1" )) -done - -for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do - path_uniq="${path_uniq//__REPLACED__SPACE__/ }" - - pushd "$path_uniq" > /dev/null - if [[ -n "$(find . -maxdepth 1 -name '*.tf' -print -quit)" ]] ; then - if ! terraform validate -check-variables=false ; then - echo - echo "Failed path: $path_uniq" - echo "================================" - fi - fi - popd > /dev/null -done From 060249f2fe4df62f413ae412eff40820560e21de Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Mon, 17 Jun 2019 13:12:16 +0200 Subject: [PATCH 031/214] Updated CHANGELOG --- CHANGELOG.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc5a04e33..5d79b6ab2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,16 @@ + +## [v1.14.0] - 2019-06-17 + +- Upgraded to work with Terraform >= 0.12 ([#44](https://github.com/antonbabenko/pre-commit-terraform/issues/44)) + + ## [v1.13.0] - 2019-06-17 +- Updated CHANGELOG - Added support for terraform_docs for Terraform 0.12 ([#45](https://github.com/antonbabenko/pre-commit-terraform/issues/45)) @@ -154,7 +161,8 @@ - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.13.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.14.0...HEAD +[v1.14.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.13.0...v1.14.0 [v1.13.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.12.0...v1.13.0 [v1.12.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.11.0...v1.12.0 [v1.11.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.10.0...v1.11.0 From d678da98103da31aee9c05b07b6ca9f68191e061 Mon Sep 17 00:00:00 2001 From: Leonhardt Wille Date: Mon, 17 Jun 2019 14:01:24 +0200 Subject: [PATCH 032/214] Fix version in README.md (#46) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 370ac9eff..667236005 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Step into the repository you want to have the pre-commit hooks installed and run ```bash cat < .pre-commit-config.yaml - repo: git://github.com/antonbabenko/pre-commit-terraform - rev: v1.13.0 + rev: v1.14.0 hooks: - id: terraform_fmt - id: terraform_docs From 10854fcfa2202bd677446b2d298a0ea6119941fb Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Tue, 18 Jun 2019 13:58:49 +0200 Subject: [PATCH 033/214] Fixed awk script for terraform-docs (kudos @cytopia) and mktemp on Mac (closes #47, #48, #49) --- terraform_docs.sh | 54 +++++++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/terraform_docs.sh b/terraform_docs.sh index 191a4a2bd..06e6f33e5 100755 --- a/terraform_docs.sh +++ b/terraform_docs.sh @@ -30,10 +30,10 @@ main() { if [[ "$hack_terraform_docs" == "1" ]]; then which awk 2>&1 >/dev/null || ( echo "awk is required for terraform-docs hack to work with Terraform 0.12"; exit 1) - TMP_AWK_FILE="$(mktemp --tmpdir terraform-docs-XXXXXXXXXX.awk)" - terraform_docs_awk $TMP_AWK_FILE - terraform_docs "$TMP_AWK_FILE" "$args" "$files" - rm -f "$TMP_AWK_FILE" + tmp_file_awk=$(mktemp "${TMPDIR:-/tmp}terraform-docs-XXXXXXXXXX") + terraform_docs_awk "$tmp_file_awk" + terraform_docs "$tmp_file_awk" "$args" "$files" + rm -f "$tmp_file_awk" else terraform_docs "0" "$args" "$files" fi @@ -78,10 +78,14 @@ terraform_docs() { if [[ "$terraform_docs_awk_file" == "0" ]]; then terraform-docs $args md ./ > "$tmp_file" else - TMP_FILE="$(mktemp --tmpdir terraform-docs-XXXXXXXXXX.tf)" - awk -f "$terraform_docs_awk_file" ./*.tf > "$TMP_FILE" - terraform-docs $args md "$TMP_FILE" > "$tmp_file" - rm -f "$TMP_FILE" + # Can't append extension for mktemp, so renaming instead + tmp_file_docs=$(mktemp "${TMPDIR:-/tmp}terraform-docs-XXXXXXXXXX") + mv "$tmp_file_docs" "$tmp_file_docs.tf" + tmp_file_docs_tf="$tmp_file_docs.tf" + + awk -f "$terraform_docs_awk_file" ./*.tf > "$tmp_file_docs_tf" + terraform-docs $args md "$tmp_file_docs_tf" > "$tmp_file" + rm -f "$tmp_file_docs_tf" fi # Replace content between markers with the placeholder - https://stackoverflow.com/questions/1212799/how-do-i-extract-lines-between-two-line-delimiters-in-perl#1212834 @@ -105,17 +109,21 @@ terraform_docs_awk() { # https://github.com/segmentio/terraform-docs/ # https://github.com/segmentio/terraform-docs/issues/62 +# Script was originally found here: https://github.com/cloudposse/build-harness/blob/master/bin/terraform-docs.awk + { - if ( /\{/ ) { + if ( $0 ~ /\{/ ) { braceCnt++ } - if ( /\}/ ) { + if ( $0 ~ /\}/ ) { braceCnt-- } # [START] variable or output block started - if ($0 ~ /(variable|output) "(.*?)"/) { + if ($0 ~ /^[[:space:]]*(variable|output)[[:space:]][[:space:]]*"(.*?)"/) { + # Normalize the braceCnt (should be 1 now) + braceCnt = 1 # [CLOSE] "default" block if (blockDefCnt > 0) { blockDefCnt = 0 @@ -126,9 +134,11 @@ terraform_docs_awk() { # [START] multiline default statement started if (blockCnt > 0) { - if ($1 == "default") { - print $0 - if ($NF ~ /[\[\(\{]/) { + if ($0 ~ /^[[:space:]][[:space:]]*(default)[[:space:]][[:space:]]*=/) { + if ($3 ~ "null") { + print " default = \"null\"" + } else { + print $0 blockDefCnt++ blockDefStart=1 } @@ -136,19 +146,21 @@ terraform_docs_awk() { } # [PRINT] single line "description" - if (blockDefCnt == 0) { - if ($1 == "description") { - # [CLOSE] "default" block - if (blockDefCnt > 0) { - blockDefCnt = 0 + if (blockCnt > 0) { + if (blockDefCnt == 0) { + if ($0 ~ /^[[:space:]][[:space:]]*description[[:space:]][[:space:]]*=/) { + # [CLOSE] "default" block + if (blockDefCnt > 0) { + blockDefCnt = 0 + } + print $0 } - print $0 } } # [PRINT] single line "type" if (blockCnt > 0) { - if ($1 == "type" ) { + if ($0 ~ /^[[:space:]][[:space:]]*type[[:space:]][[:space:]]*=/ ) { # [CLOSE] "default" block if (blockDefCnt > 0) { blockDefCnt = 0 From 59ffa65527d7628b0f87b665c167643515940a2f Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Tue, 18 Jun 2019 14:00:29 +0200 Subject: [PATCH 034/214] Updated CHANGELOG --- CHANGELOG.md | 11 ++++++++++- README.md | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d79b6ab2..935f1913c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,17 @@ + +## [v1.15.0] - 2019-06-18 + +- Fixed awk script for terraform-docs (kudos [@cytopia](https://github.com/cytopia)) and mktemp on Mac (closes [#47](https://github.com/antonbabenko/pre-commit-terraform/issues/47), [#48](https://github.com/antonbabenko/pre-commit-terraform/issues/48), [#49](https://github.com/antonbabenko/pre-commit-terraform/issues/49)) +- Fix version in README.md ([#46](https://github.com/antonbabenko/pre-commit-terraform/issues/46)) + + ## [v1.14.0] - 2019-06-17 +- Updated CHANGELOG - Upgraded to work with Terraform >= 0.12 ([#44](https://github.com/antonbabenko/pre-commit-terraform/issues/44)) @@ -161,7 +169,8 @@ - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.14.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.15.0...HEAD +[v1.15.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.14.0...v1.15.0 [v1.14.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.13.0...v1.14.0 [v1.13.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.12.0...v1.13.0 [v1.12.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.11.0...v1.12.0 diff --git a/README.md b/README.md index 667236005..1ff6d400a 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Step into the repository you want to have the pre-commit hooks installed and run ```bash cat < .pre-commit-config.yaml - repo: git://github.com/antonbabenko/pre-commit-terraform - rev: v1.14.0 + rev: v1.15.0 hooks: - id: terraform_fmt - id: terraform_docs From c9ecd72f5f5014b983ca2e87c431cbb8b2cc7eed Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Tue, 18 Jun 2019 21:17:57 +0200 Subject: [PATCH 035/214] Add slash to mktemp dir (fixed #50) --- terraform_docs.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/terraform_docs.sh b/terraform_docs.sh index 06e6f33e5..464dc946d 100755 --- a/terraform_docs.sh +++ b/terraform_docs.sh @@ -30,7 +30,7 @@ main() { if [[ "$hack_terraform_docs" == "1" ]]; then which awk 2>&1 >/dev/null || ( echo "awk is required for terraform-docs hack to work with Terraform 0.12"; exit 1) - tmp_file_awk=$(mktemp "${TMPDIR:-/tmp}terraform-docs-XXXXXXXXXX") + tmp_file_awk=$(mktemp "${TMPDIR:-/tmp}/terraform-docs-XXXXXXXXXX") terraform_docs_awk "$tmp_file_awk" terraform_docs "$tmp_file_awk" "$args" "$files" rm -f "$tmp_file_awk" @@ -79,7 +79,7 @@ terraform_docs() { terraform-docs $args md ./ > "$tmp_file" else # Can't append extension for mktemp, so renaming instead - tmp_file_docs=$(mktemp "${TMPDIR:-/tmp}terraform-docs-XXXXXXXXXX") + tmp_file_docs=$(mktemp "${TMPDIR:-/tmp}/terraform-docs-XXXXXXXXXX") mv "$tmp_file_docs" "$tmp_file_docs.tf" tmp_file_docs_tf="$tmp_file_docs.tf" From 5d8d926848047866342fd9e211f3f81c5157cdfd Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Tue, 18 Jun 2019 21:18:18 +0200 Subject: [PATCH 036/214] Updated CHANGELOG --- CHANGELOG.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 935f1913c..5522d64af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,16 @@ + +## [v1.16.0] - 2019-06-18 + +- Add slash to mktemp dir (fixed [#50](https://github.com/antonbabenko/pre-commit-terraform/issues/50)) + + ## [v1.15.0] - 2019-06-18 +- Updated CHANGELOG - Fixed awk script for terraform-docs (kudos [@cytopia](https://github.com/cytopia)) and mktemp on Mac (closes [#47](https://github.com/antonbabenko/pre-commit-terraform/issues/47), [#48](https://github.com/antonbabenko/pre-commit-terraform/issues/48), [#49](https://github.com/antonbabenko/pre-commit-terraform/issues/49)) - Fix version in README.md ([#46](https://github.com/antonbabenko/pre-commit-terraform/issues/46)) @@ -169,7 +176,8 @@ - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.15.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.16.0...HEAD +[v1.16.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.15.0...v1.16.0 [v1.15.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.14.0...v1.15.0 [v1.14.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.13.0...v1.14.0 [v1.13.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.12.0...v1.13.0 From 3147a7c0ee5d06d783346a968aa2c0c4e104608b Mon Sep 17 00:00:00 2001 From: Eric Gonzales Date: Wed, 19 Jun 2019 06:10:21 -0400 Subject: [PATCH 037/214] Fix typo in README (#51) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1ff6d400a..c05352dec 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ Check the [source file](https://github.com/antonbabenko/pre-commit-terraform/blo ## Notes for developers 1. Python hooks are supported now too. All you have to do is: - 1. add a line to the `console_sripts` array in `entry_points` in `setup.py` + 1. add a line to the `console_scripts` array in `entry_points` in `setup.py` 1. Put your python script in the `pre_commit_hooks` folder Enjoy the clean and documented code! From 83629157c22800f59ab59642f82abef3df31721a Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Tue, 25 Jun 2019 14:34:46 +0200 Subject: [PATCH 038/214] Fixed enquoted types in terraform_docs (fixed #52) --- terraform_docs.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/terraform_docs.sh b/terraform_docs.sh index 464dc946d..0935b69e6 100755 --- a/terraform_docs.sh +++ b/terraform_docs.sh @@ -169,7 +169,12 @@ terraform_docs_awk() { if (type ~ "object") { print " type = \"object\"" } else { - print " type = \"" $3 "\"" + # legacy quoted types: "string", "list", and "map" + if ($3 ~ /^[[:space:]]*"(.*?)"[[:space:]]*$/) { + print " type = " $3 + } else { + print " type = \"" $3 "\"" + } } } } From 4bebeac734da116cc5fe535643252724321521a7 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Tue, 25 Jun 2019 14:35:00 +0200 Subject: [PATCH 039/214] Updated CHANGELOG --- CHANGELOG.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5522d64af..ebbb6fba7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,16 @@ + +## [v1.17.0] - 2019-06-25 + +- Fixed enquoted types in terraform_docs (fixed [#52](https://github.com/antonbabenko/pre-commit-terraform/issues/52)) + + ## [v1.16.0] - 2019-06-18 +- Updated CHANGELOG - Add slash to mktemp dir (fixed [#50](https://github.com/antonbabenko/pre-commit-terraform/issues/50)) @@ -176,7 +183,8 @@ - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.16.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.17.0...HEAD +[v1.17.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.16.0...v1.17.0 [v1.16.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.15.0...v1.16.0 [v1.15.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.14.0...v1.15.0 [v1.14.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.13.0...v1.14.0 From d8dfc2c0345a8a846ca7841e4bd58b7deab91810 Mon Sep 17 00:00:00 2001 From: Scott Crooks Date: Tue, 20 Aug 2019 20:38:40 +0200 Subject: [PATCH 040/214] Formatter for Terragrunt HCL files (#60) * Formatter for Terragrunt HCL files * Adding Terragrunt documentation --- .pre-commit-hooks.yaml | 8 ++++++++ README.md | 1 + terragrunt_fmt.sh | 23 +++++++++++++++++++++++ 3 files changed, 32 insertions(+) create mode 100755 terragrunt_fmt.sh diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index ee10f8228..789d7095a 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -41,3 +41,11 @@ language: script files: (\.tf|\.tfvars)$ exclude: \.terraform\/.*$ + +- id: terragrunt_fmt + name: Terragrunt fmt + description: Rewrites all Terragrunt configuration files to a canonical format. + entry: terragrunt_fmt.sh + language: script + files: (\.hcl)$ + exclude: \.terraform\/.*$ diff --git a/README.md b/README.md index c05352dec..539195b70 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ There are several [pre-commit](http://pre-commit.com/) hooks to keep Terraform c * `terraform_docs` - Inserts input and output documentation into `README.md`. Recommended. * `terraform_docs_without_aggregate_type_defaults` - Inserts input and output documentation into `README.md` without aggregate type defaults. * `terraform_docs_replace` - Runs `terraform-docs` and pipes the output directly to README.md +* `terragrunt_fmt` - Rewrites all Terragrunt configuration files to a canonical format. Check the [source file](https://github.com/antonbabenko/pre-commit-terraform/blob/master/.pre-commit-hooks.yaml) to know arguments used for each hook. diff --git a/terragrunt_fmt.sh b/terragrunt_fmt.sh new file mode 100755 index 000000000..ee23131e7 --- /dev/null +++ b/terragrunt_fmt.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +set -e + +declare -a paths + +index=0 + +for file_with_path in "$@"; do + file_with_path="${file_with_path// /__REPLACED__SPACE__}" + + paths[index]=$(dirname "$file_with_path") + + let "index+=1" +done + +for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do + path_uniq="${path_uniq//__REPLACED__SPACE__/ }" + + pushd "$path_uniq" > /dev/null + terragrunt hclfmt + popd > /dev/null +done From 7eb805fb0e1f2acb5dbfe8b04ff72fb3b31f5a2c Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Tue, 20 Aug 2019 21:16:30 +0200 Subject: [PATCH 041/214] Updated README with terragrunt_fmt hook --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 539195b70..baca5ebcf 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Step into the repository you want to have the pre-commit hooks installed and run ```bash cat < .pre-commit-config.yaml - repo: git://github.com/antonbabenko/pre-commit-terraform - rev: v1.15.0 + rev: v1.18.0 hooks: - id: terraform_fmt - id: terraform_docs @@ -46,13 +46,13 @@ pre-commit run -a ## Available Hooks -There are several [pre-commit](http://pre-commit.com/) hooks to keep Terraform configurations (both `*.tf` and `*.tfvars`) in a good shape: +There are several [pre-commit](http://pre-commit.com/) hooks to keep Terraform configurations (both `*.tf` and `*.tfvars`) and Terragrunt configurations (`*.hcl`) in a good shape: * `terraform_fmt` - Rewrites all Terraform configuration files to a canonical format. * `terraform_validate` - Validates all Terraform configuration files. * `terraform_docs` - Inserts input and output documentation into `README.md`. Recommended. * `terraform_docs_without_aggregate_type_defaults` - Inserts input and output documentation into `README.md` without aggregate type defaults. * `terraform_docs_replace` - Runs `terraform-docs` and pipes the output directly to README.md -* `terragrunt_fmt` - Rewrites all Terragrunt configuration files to a canonical format. +* `terragrunt_fmt` - Rewrites all Terragrunt configuration files (`*.hcl`) to a canonical format. Check the [source file](https://github.com/antonbabenko/pre-commit-terraform/blob/master/.pre-commit-hooks.yaml) to know arguments used for each hook. From dc8cf4844190ce81f214485917639d7cf7c308b9 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Tue, 20 Aug 2019 21:17:41 +0200 Subject: [PATCH 042/214] Updated CHANGELOG --- CHANGELOG.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ebbb6fba7..a5681f642 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,10 +3,19 @@ + +## [v1.18.0] - 2019-08-20 + +- Updated README with terragrunt_fmt hook +- Formatter for Terragrunt HCL files ([#60](https://github.com/antonbabenko/pre-commit-terraform/issues/60)) + + ## [v1.17.0] - 2019-06-25 +- Updated CHANGELOG - Fixed enquoted types in terraform_docs (fixed [#52](https://github.com/antonbabenko/pre-commit-terraform/issues/52)) +- Fix typo in README ([#51](https://github.com/antonbabenko/pre-commit-terraform/issues/51)) @@ -183,7 +192,8 @@ - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.17.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.18.0...HEAD +[v1.18.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.17.0...v1.18.0 [v1.17.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.16.0...v1.17.0 [v1.16.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.15.0...v1.16.0 [v1.15.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.14.0...v1.15.0 From e0d3d614225017064ad47921ced90f04f7e15bd5 Mon Sep 17 00:00:00 2001 From: Costin GALAN Date: Tue, 20 Aug 2019 22:31:28 +0300 Subject: [PATCH 043/214] Added support for TFLint with --deep parameter (#53) Added support for TFLint (https://github.com/wata727/tflint). Signed-off-by: Costin Galan --- .pre-commit-hooks.yaml | 8 ++++++++ terraform_tflint.sh | 23 +++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100755 terraform_tflint.sh diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 789d7095a..c7f403941 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -42,6 +42,14 @@ files: (\.tf|\.tfvars)$ exclude: \.terraform\/.*$ +- id: terraform_tflint + name: Terraform validate with tflint + description: Validates all Terraform configuration files with TFLint. + entry: terraform_tflint.sh + language: script + files: (\.tf|\.tfvars)$ + exclude: \.terraform\/.*$ + - id: terragrunt_fmt name: Terragrunt fmt description: Rewrites all Terragrunt configuration files to a canonical format. diff --git a/terraform_tflint.sh b/terraform_tflint.sh new file mode 100755 index 000000000..4e6c23e76 --- /dev/null +++ b/terraform_tflint.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +set -e + +declare -a paths +declare -a tfvars_files + +index=0 + +for file_with_path in "$@"; do + file_with_path="${file_with_path// /__REPLACED__SPACE__}" + + paths[index]=$(dirname "$file_with_path") + + let "index+=1" +done + +for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do + path_uniq="${path_uniq//__REPLACED__SPACE__/ }" + + pushd "$path_uniq" > /dev/null + tflint --deep + popd > /dev/null +done From d5640fd4dc0e07ab4276409c1e8d2e8f9f1f1ddc Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Tue, 20 Aug 2019 21:34:42 +0200 Subject: [PATCH 044/214] Updated README with terraform_tflint hook --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index baca5ebcf..6637beec6 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ There are several [pre-commit](http://pre-commit.com/) hooks to keep Terraform c * `terraform_docs` - Inserts input and output documentation into `README.md`. Recommended. * `terraform_docs_without_aggregate_type_defaults` - Inserts input and output documentation into `README.md` without aggregate type defaults. * `terraform_docs_replace` - Runs `terraform-docs` and pipes the output directly to README.md +* `terraform_tflint` - Validates all Terraform configuration files with [TFLint](https://github.com/wata727/tflint). * `terragrunt_fmt` - Rewrites all Terragrunt configuration files (`*.hcl`) to a canonical format. Check the [source file](https://github.com/antonbabenko/pre-commit-terraform/blob/master/.pre-commit-hooks.yaml) to know arguments used for each hook. From c823172b8436519494f4b466b0eac3929697b4ed Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Tue, 20 Aug 2019 21:34:52 +0200 Subject: [PATCH 045/214] Updated CHANGELOG --- CHANGELOG.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5681f642..8496bac9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,17 @@ + +## [v1.19.0] - 2019-08-20 + +- Updated README with terraform_tflint hook +- Added support for TFLint with --deep parameter ([#53](https://github.com/antonbabenko/pre-commit-terraform/issues/53)) + + ## [v1.18.0] - 2019-08-20 +- Updated CHANGELOG - Updated README with terragrunt_fmt hook - Formatter for Terragrunt HCL files ([#60](https://github.com/antonbabenko/pre-commit-terraform/issues/60)) @@ -192,7 +200,8 @@ - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.18.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.19.0...HEAD +[v1.19.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.18.0...v1.19.0 [v1.18.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.17.0...v1.18.0 [v1.17.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.16.0...v1.17.0 [v1.16.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.15.0...v1.16.0 From a7b730a20b7ecb0f86a513430e6692f9ba1c387d Mon Sep 17 00:00:00 2001 From: Dave Gallant Date: Sat, 12 Oct 2019 05:39:06 -0400 Subject: [PATCH 046/214] Update rev in README.md (#70) Updating the version in the README. In order for `terraform_tflint`, the rev must be at least `v1.19.0`. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6637beec6..d61fe891b 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Step into the repository you want to have the pre-commit hooks installed and run ```bash cat < .pre-commit-config.yaml - repo: git://github.com/antonbabenko/pre-commit-terraform - rev: v1.18.0 + rev: v1.19.0 hooks: - id: terraform_fmt - id: terraform_docs From eec9c884a1fed9ef87aa71ac14031898c14323f4 Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Thu, 17 Oct 2019 14:50:16 +0300 Subject: [PATCH 047/214] Improve installation instructions and make README more readable (#72) --- README.md | 68 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index d61fe891b..ec268998f 100644 --- a/README.md +++ b/README.md @@ -4,21 +4,42 @@ ## How to install -### Step 1 +### 1. Install dependencies -On MacOSX install the `pre-commit` and `awk` (required for Terraform 0.12) package +* [`pre-commit`](http://pre-commit.com/#install) +* [`terraform-docs`](https://github.com/segmentio/terraform-docs) (required for `terraform_docs` hooks) +* GNU `awk` (required for `terraform_docs` hooks in Terraform 0.12) +* [`TFLint`](https://github.com/wata727/tflint) (required for `terraform_tflint` hook) + +##### MacOS + +```bash +brew install pre-commit awk terraform-docs tflint +``` + +##### Ubuntu ```bash -brew install pre-commit awk +sudo apt install python-pip3 gawk &&\ +pip3 install pre-commit +curl -L "$(curl -s https://api.github.com/repos/segmentio/terraform-docs/releases/latest | grep -o -E "https://.+?-linux-amd64")" > terraform-docs && chmod +x terraform-docs && sudo mv terraform-docs /usr/bin/ +curl -L "$(curl -s https://api.github.com/repos/wata727/tflint/releases/latest | grep -o -E "https://.+?_linux_amd64.zip")" > tflint.zip && unzip tflint.zip && rm tflint.zip && sudo mv tflint /usr/bin/ ``` -For other operating systems check the [official documentation](http://pre-commit.com/#install) +### 2. Install the pre-commit hook globally + +```bash +DIR=~/.git-template +git config --global init.templateDir ${DIR} +pre-commit init-templatedir -t pre-commit ${DIR} +``` -### Step 2 +### 3. Add configs and hooks Step into the repository you want to have the pre-commit hooks installed and run: ```bash +git init cat < .pre-commit-config.yaml - repo: git://github.com/antonbabenko/pre-commit-terraform rev: v1.19.0 @@ -28,15 +49,7 @@ cat < .pre-commit-config.yaml EOF ``` -### Step 3 - -Install the pre-commit hook - -```bash -pre-commit install -``` - -### Step 4 +### 4. Run After pre-commit hook has been installed you can run it manually on all files in the repository @@ -47,19 +60,28 @@ pre-commit run -a ## Available Hooks There are several [pre-commit](http://pre-commit.com/) hooks to keep Terraform configurations (both `*.tf` and `*.tfvars`) and Terragrunt configurations (`*.hcl`) in a good shape: -* `terraform_fmt` - Rewrites all Terraform configuration files to a canonical format. -* `terraform_validate` - Validates all Terraform configuration files. -* `terraform_docs` - Inserts input and output documentation into `README.md`. Recommended. -* `terraform_docs_without_aggregate_type_defaults` - Inserts input and output documentation into `README.md` without aggregate type defaults. -* `terraform_docs_replace` - Runs `terraform-docs` and pipes the output directly to README.md -* `terraform_tflint` - Validates all Terraform configuration files with [TFLint](https://github.com/wata727/tflint). -* `terragrunt_fmt` - Rewrites all Terragrunt configuration files (`*.hcl`) to a canonical format. + +| Hook name | Description | +| ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- | +| `terraform_fmt` | Rewrites all Terraform configuration files to a canonical format. | +| `terraform_validate` | Validates all Terraform configuration files. | +| `terraform_docs` | Inserts input and output documentation into `README.md`. Recommended. | +| `terraform_docs_without_aggregate_type_defaults` | Inserts input and output documentation into `README.md` without aggregate type defaults. | +| `terraform_docs_replace` | Runs `terraform-docs` and pipes the output directly to README.md | +| `terraform_tflint` | Validates all Terraform configuration files with [TFLint](https://github.com/wata727/tflint). | +| `terragrunt_fmt` | Rewrites all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) to a canonical format. | Check the [source file](https://github.com/antonbabenko/pre-commit-terraform/blob/master/.pre-commit-hooks.yaml) to know arguments used for each hook. -## Notes about hooks +## Notes about terraform_docs hooks -1. `terraform_docs` and `terraform_docs_without_aggregate_type_defaults` will insert/update documentation generated by [terraform-docs](https://github.com/segmentio/terraform-docs) between markers - `` and `` if they are present in `README.md`. Make sure that `terraform-docs` is installed. +1. `terraform_docs` and `terraform_docs_without_aggregate_type_defaults` will insert/update documentation generated by [terraform-docs](https://github.com/segmentio/terraform-docs) framed by markers: +```txt + + + +``` +if they are present in `README.md`. 1. `terraform_docs_replace` replaces the entire README.md rather than doing string replacement between markers. Put your additional documentation at the top of your `main.tf` for it to be pulled in. The optional `--dest` argument lets you change the name of the file that gets created/modified. From 657ce4ecf019b6964813a590fd0c3ed35e234a43 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Fri, 1 Nov 2019 10:08:49 +0100 Subject: [PATCH 048/214] Added FUNDING.yml --- .github/FUNDING.yml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..9fdcfce62 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: [antonbabenko] +custom: https://www.paypal.me/antonbabenko From 6ffc42a4b5c1f194b9feacfa33e308ea7ea569b4 Mon Sep 17 00:00:00 2001 From: cytopia Date: Sat, 2 Nov 2019 12:15:33 +0100 Subject: [PATCH 049/214] Fixes #65: terraform-docs should not fail if complex types contain 'description' keyword (#73) --- terraform_docs.sh | 165 +++++++++++++++++++++++++++++++++------------- 1 file changed, 120 insertions(+), 45 deletions(-) diff --git a/terraform_docs.sh b/terraform_docs.sh index 0935b69e6..958272eee 100755 --- a/terraform_docs.sh +++ b/terraform_docs.sh @@ -120,81 +120,156 @@ terraform_docs_awk() { 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 (should be 1 now) + # Normalize the braceCnt and block (should be 1 now) braceCnt = 1 - # [CLOSE] "default" block - if (blockDefCnt > 0) { - blockDefCnt = 0 - } - blockCnt++ + blockCnt = 1 + # [CLOSE] "default" and "type" block + blockDefaultCnt = 0 + blockTypeCnt = 0 + # Print variable|output line print $0 } - # [START] multiline default statement started - if (blockCnt > 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 - blockDefCnt++ - blockDefStart=1 + # Count opening blocks + blockDefaultCnt += gsub(/\(/, "") + blockDefaultCnt += gsub(/\[/, "") + blockDefaultCnt += gsub(/\{/, "") + # Count closing blocks + blockDefaultCnt -= gsub(/\)/, "") + blockDefaultCnt -= gsub(/\]/, "") + blockDefaultCnt -= gsub(/\}/, "") } } } - # [PRINT] single line "description" - if (blockCnt > 0) { - if (blockDefCnt == 0) { - if ($0 ~ /^[[:space:]][[:space:]]*description[[:space:]][[:space:]]*=/) { - # [CLOSE] "default" block - if (blockDefCnt > 0) { - blockDefCnt = 0 - } - print $0 - } - } - } - # [PRINT] single line "type" - if (blockCnt > 0) { - if ($0 ~ /^[[:space:]][[:space:]]*type[[:space:]][[:space:]]*=/ ) { - # [CLOSE] "default" block - if (blockDefCnt > 0) { - blockDefCnt = 0 - } - type=$3 - if (type ~ "object") { + # ---------------------------------------------------------------------------------------------- + # 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 ($3 ~ /^[[:space:]]*"(.*?)"[[:space:]]*$/) { - print " type = " $3 + if (type ~ /^[[:space:]]*"(.*?)"[[:space:]]*$/) { + print " type = " type } else { - print " type = \"" $3 "\"" + print " type = \"" type "\"" } } + # Count opening blocks + blockTypeCnt += gsub(/\(/, "") + blockTypeCnt += gsub(/\[/, "") + blockTypeCnt += gsub(/\{/, "") + # Count closing blocks + blockTypeCnt -= gsub(/\)/, "") + blockTypeCnt -= gsub(/\]/, "") + blockTypeCnt -= gsub(/\}/, "") } } - # [CLOSE] variable/output block - if (blockCnt > 0) { - if (braceCnt == 0 && blockCnt > 0) { - blockCnt-- + + # ---------------------------------------------------------------------------------------------- + # description = ... + # ---------------------------------------------------------------------------------------------- + # [PRINT] single line "description" + if (blockCnt > 0 && blockTypeCnt == 0 && blockDefaultCnt == 0) { + if ($0 ~ /^[[:space:]][[:space:]]*description[[:space:]][[:space:]]*=/) { print $0 } } - # [PRINT] Multiline "default" statement - if (blockCnt > 0 && blockDefCnt > 0) { - if (blockDefStart == 1) { - blockDefStart = 0 - } else { - 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 From cbf245833b4194520b04c0aa649973ea0df5cd71 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Sat, 2 Nov 2019 12:16:31 +0100 Subject: [PATCH 050/214] Updated CHANGELOG --- CHANGELOG.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8496bac9a..c2b780b73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,19 @@ + +## [v1.20.0] - 2019-11-02 + +- Fixes [#65](https://github.com/antonbabenko/pre-commit-terraform/issues/65): terraform-docs should not fail if complex types contain 'description' keyword ([#73](https://github.com/antonbabenko/pre-commit-terraform/issues/73)) +- Added FUNDING.yml +- Improve installation instructions and make README more readable ([#72](https://github.com/antonbabenko/pre-commit-terraform/issues/72)) +- Update rev in README.md ([#70](https://github.com/antonbabenko/pre-commit-terraform/issues/70)) + + ## [v1.19.0] - 2019-08-20 +- Updated CHANGELOG - Updated README with terraform_tflint hook - Added support for TFLint with --deep parameter ([#53](https://github.com/antonbabenko/pre-commit-terraform/issues/53)) @@ -200,7 +210,8 @@ - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.19.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.20.0...HEAD +[v1.20.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.19.0...v1.20.0 [v1.19.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.18.0...v1.19.0 [v1.18.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.17.0...v1.18.0 [v1.17.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.16.0...v1.17.0 From 8bddc8b81cc2739b2af2f6932204108ee02f6327 Mon Sep 17 00:00:00 2001 From: chopped pork Date: Sat, 16 Nov 2019 18:37:23 +0000 Subject: [PATCH 051/214] use getopt for args in the tflint hook, following the approach in terraform-docs (#75) --- CHANGELOG.md | 2 +- README.md | 11 + terraform_tflint.sh | 539 ++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 537 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2b780b73..f74635fd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ ## [Unreleased] - +- Changes TFLint hook to not use --deep by default but allow enabling it (along with other parameters) via `args` ([#71](https://github.com/antonbabenko/pre-commit-terraform/issues/71)) ## [v1.20.0] - 2019-11-02 diff --git a/README.md b/README.md index ec268998f..355ff9711 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,17 @@ if they are present in `README.md`. 1. `terraform-docs` works with Terraform 0.12 but support is hackish (it requires `awk` to be installed) and may contain bugs. You can follow the native support of Terraform 0.12 in `terraform-docs` in [issue #62](https://github.com/segmentio/terraform-docs/issues/62). +## Notes about terraform_tflint hooks + +1. `terraform_tflint` supports custom arguments so you can enable module inspection, deep check mode etc. + + 1. Example: + ```yaml + hooks: + - id: terraform_tflint + args: ['--deep'] + ``` + ## Notes for developers 1. Python hooks are supported now too. All you have to do is: diff --git a/terraform_tflint.sh b/terraform_tflint.sh index 4e6c23e76..2393c3836 100755 --- a/terraform_tflint.sh +++ b/terraform_tflint.sh @@ -1,23 +1,534 @@ #!/usr/bin/env bash set -e -declare -a paths -declare -a tfvars_files +main() { + declare argv + argv=$(getopt -o a: --long args: -- "$@") || return + eval "set -- $argv" -index=0 + declare args + declare files -for file_with_path in "$@"; do - file_with_path="${file_with_path// /__REPLACED__SPACE__}" + for argv; do + case $argv in + (-a|--args) + shift + args="$1" + shift + ;; + (--) + shift + files="$@" + break + ;; + esac + done - paths[index]=$(dirname "$file_with_path") + tflint_ "$args" "$files" +} - let "index+=1" -done +tflint_() { + for file_with_path in "$@"; do + file_with_path="${file_with_path// /__REPLACED__SPACE__}" -for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do - path_uniq="${path_uniq//__REPLACED__SPACE__/ }" + paths[index]=$(dirname "$file_with_path") - pushd "$path_uniq" > /dev/null - tflint --deep - popd > /dev/null -done + let "index+=1" + done + + for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do + path_uniq="${path_uniq//__REPLACED__SPACE__/ }" + + pushd "$path_uniq" > /dev/null + tflint $args + popd > /dev/null + done +} + +getopt() { + # pure-getopt, a drop-in replacement for GNU getopt in pure Bash. + # version 1.4.3 + # + # Copyright 2012-2018 Aron Griffis + # + # Permission is hereby granted, free of charge, to any person obtaining + # a copy of this software and associated documentation files (the + # "Software"), to deal in the Software without restriction, including + # without limitation the rights to use, copy, modify, merge, publish, + # distribute, sublicense, and/or sell copies of the Software, and to + # permit persons to whom the Software is furnished to do so, subject to + # the following conditions: + # + # The above copyright notice and this permission notice shall be included + # in all copies or substantial portions of the Software. + # + # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + _getopt_main() { + # Returns one of the following statuses: + # 0 success + # 1 error parsing parameters + # 2 error in getopt invocation + # 3 internal error + # 4 reserved for -T + # + # For statuses 0 and 1, generates normalized and shell-quoted + # "options -- parameters" on stdout. + + declare parsed status + declare short long name flags + declare have_short=false + + # Synopsis from getopt man-page: + # + # getopt optstring parameters + # getopt [options] [--] optstring parameters + # getopt [options] -o|--options optstring [options] [--] parameters + # + # The first form can be normalized to the third form which + # _getopt_parse() understands. The second form can be recognized after + # first parse when $short hasn't been set. + + if [[ -n ${GETOPT_COMPATIBLE+isset} || $1 == [^-]* ]]; then + # Enable compatibility mode + flags=c$flags + # Normalize first to third synopsis form + set -- -o "$1" -- "${@:2}" + fi + + # First parse always uses flags=p since getopt always parses its own + # arguments effectively in this mode. + parsed=$(_getopt_parse getopt ahl:n:o:qQs:TuV \ + alternative,help,longoptions:,name:,options:,quiet,quiet-output,shell:,test,version \ + p "$@") + status=$? + if [[ $status != 0 ]]; then + if [[ $status == 1 ]]; then + echo "Try \`getopt --help' for more information." >&2 + # Since this is the first parse, convert status 1 to 2 + status=2 + fi + return $status + fi + eval "set -- $parsed" + + while [[ $# -gt 0 ]]; do + case $1 in + (-a|--alternative) + flags=a$flags ;; + + (-h|--help) + _getopt_help + return 2 # as does GNU getopt + ;; + + (-l|--longoptions) + long="$long${long:+,}$2" + shift ;; + + (-n|--name) + name=$2 + shift ;; + + (-o|--options) + short=$2 + have_short=true + shift ;; + + (-q|--quiet) + flags=q$flags ;; + + (-Q|--quiet-output) + flags=Q$flags ;; + + (-s|--shell) + case $2 in + (sh|bash) + flags=${flags//t/} ;; + (csh|tcsh) + flags=t$flags ;; + (*) + echo 'getopt: unknown shell after -s or --shell argument' >&2 + echo "Try \`getopt --help' for more information." >&2 + return 2 ;; + esac + shift ;; + + (-u|--unquoted) + flags=u$flags ;; + + (-T|--test) + return 4 ;; + + (-V|--version) + echo "pure-getopt 1.4.3" + return 0 ;; + + (--) + shift + break ;; + esac + + shift + done + + if ! $have_short; then + # $short was declared but never set, not even to an empty string. + # This implies the second form in the synopsis. + if [[ $# == 0 ]]; then + echo 'getopt: missing optstring argument' >&2 + echo "Try \`getopt --help' for more information." >&2 + return 2 + fi + short=$1 + have_short=true + shift + fi + + if [[ $short == -* ]]; then + # Leading dash means generate output in place rather than reordering, + # unless we're already in compatibility mode. + [[ $flags == *c* ]] || flags=i$flags + short=${short#?} + elif [[ $short == +* ]]; then + # Leading plus means POSIXLY_CORRECT, unless we're already in + # compatibility mode. + [[ $flags == *c* ]] || flags=p$flags + short=${short#?} + fi + + # This should fire if POSIXLY_CORRECT is in the environment, even if + # it's an empty string. That's the difference between :+ and + + flags=${POSIXLY_CORRECT+p}$flags + + _getopt_parse "${name:-getopt}" "$short" "$long" "$flags" "$@" + } + + _getopt_parse() { + # Inner getopt parser, used for both first parse and second parse. + # Returns 0 for success, 1 for error parsing, 3 for internal error. + # In the case of status 1, still generates stdout with whatever could + # be parsed. + # + # $flags is a string of characters with the following meanings: + # a - alternative parsing mode + # c - GETOPT_COMPATIBLE + # i - generate output in place rather than reordering + # p - POSIXLY_CORRECT + # q - disable error reporting + # Q - disable normal output + # t - quote for csh/tcsh + # u - unquoted output + + declare name="$1" short="$2" long="$3" flags="$4" + shift 4 + + # Split $long on commas, prepend double-dashes, strip colons; + # for use with _getopt_resolve_abbrev + declare -a longarr + _getopt_split longarr "$long" + longarr=( "${longarr[@]/#/--}" ) + longarr=( "${longarr[@]%:}" ) + longarr=( "${longarr[@]%:}" ) + + # Parse and collect options and parameters + declare -a opts params + declare o alt_recycled=false error=0 + + while [[ $# -gt 0 ]]; do + case $1 in + (--) + params=( "${params[@]}" "${@:2}" ) + break ;; + + (--*=*) + o=${1%%=*} + if ! o=$(_getopt_resolve_abbrev "$o" "${longarr[@]}"); then + error=1 + elif [[ ,"$long", == *,"${o#--}"::,* ]]; then + opts=( "${opts[@]}" "$o" "${1#*=}" ) + elif [[ ,"$long", == *,"${o#--}":,* ]]; then + opts=( "${opts[@]}" "$o" "${1#*=}" ) + elif [[ ,"$long", == *,"${o#--}",* ]]; then + if $alt_recycled; then o=${o#-}; fi + _getopt_err "$name: option '$o' doesn't allow an argument" + error=1 + else + echo "getopt: assertion failed (1)" >&2 + return 3 + fi + alt_recycled=false + ;; + + (--?*) + o=$1 + if ! o=$(_getopt_resolve_abbrev "$o" "${longarr[@]}"); then + error=1 + elif [[ ,"$long", == *,"${o#--}",* ]]; then + opts=( "${opts[@]}" "$o" ) + elif [[ ,"$long", == *,"${o#--}::",* ]]; then + opts=( "${opts[@]}" "$o" '' ) + elif [[ ,"$long", == *,"${o#--}:",* ]]; then + if [[ $# -ge 2 ]]; then + shift + opts=( "${opts[@]}" "$o" "$1" ) + else + if $alt_recycled; then o=${o#-}; fi + _getopt_err "$name: option '$o' requires an argument" + error=1 + fi + else + echo "getopt: assertion failed (2)" >&2 + return 3 + fi + alt_recycled=false + ;; + + (-*) + if [[ $flags == *a* ]]; then + # Alternative parsing mode! + # Try to handle as a long option if any of the following apply: + # 1. There's an equals sign in the mix -x=3 or -xy=3 + # 2. There's 2+ letters and an abbreviated long match -xy + # 3. There's a single letter and an exact long match + # 4. There's a single letter and no short match + o=${1::2} # temp for testing #4 + if [[ $1 == *=* || $1 == -?? || \ + ,$long, == *,"${1#-}"[:,]* || \ + ,$short, != *,"${o#-}"[:,]* ]]; then + o=$(_getopt_resolve_abbrev "${1%%=*}" "${longarr[@]}" 2>/dev/null) + case $? in + (0) + # Unambiguous match. Let the long options parser handle + # it, with a flag to get the right error message. + set -- "-$1" "${@:2}" + alt_recycled=true + continue ;; + (1) + # Ambiguous match, generate error and continue. + _getopt_resolve_abbrev "${1%%=*}" "${longarr[@]}" >/dev/null + error=1 + shift + continue ;; + (2) + # No match, fall through to single-character check. + true ;; + (*) + echo "getopt: assertion failed (3)" >&2 + return 3 ;; + esac + fi + fi + + o=${1::2} + if [[ "$short" == *"${o#-}"::* ]]; then + if [[ ${#1} -gt 2 ]]; then + opts=( "${opts[@]}" "$o" "${1:2}" ) + else + opts=( "${opts[@]}" "$o" '' ) + fi + elif [[ "$short" == *"${o#-}":* ]]; then + if [[ ${#1} -gt 2 ]]; then + opts=( "${opts[@]}" "$o" "${1:2}" ) + elif [[ $# -ge 2 ]]; then + shift + opts=( "${opts[@]}" "$o" "$1" ) + else + _getopt_err "$name: option requires an argument -- '${o#-}'" + error=1 + fi + elif [[ "$short" == *"${o#-}"* ]]; then + opts=( "${opts[@]}" "$o" ) + if [[ ${#1} -gt 2 ]]; then + set -- "$o" "-${1:2}" "${@:2}" + fi + else + if [[ $flags == *a* ]]; then + # Alternative parsing mode! Report on the entire failed + # option. GNU includes =value but we omit it for sanity with + # very long values. + _getopt_err "$name: unrecognized option '${1%%=*}'" + else + _getopt_err "$name: invalid option -- '${o#-}'" + if [[ ${#1} -gt 2 ]]; then + set -- "$o" "-${1:2}" "${@:2}" + fi + fi + error=1 + fi ;; + + (*) + # GNU getopt in-place mode (leading dash on short options) + # overrides POSIXLY_CORRECT + if [[ $flags == *i* ]]; then + opts=( "${opts[@]}" "$1" ) + elif [[ $flags == *p* ]]; then + params=( "${params[@]}" "$@" ) + break + else + params=( "${params[@]}" "$1" ) + fi + esac + + shift + done + + if [[ $flags == *Q* ]]; then + true # generate no output + else + echo -n ' ' + if [[ $flags == *[cu]* ]]; then + printf '%s -- %s' "${opts[*]}" "${params[*]}" + else + if [[ $flags == *t* ]]; then + _getopt_quote_csh "${opts[@]}" -- "${params[@]}" + else + _getopt_quote "${opts[@]}" -- "${params[@]}" + fi + fi + echo + fi + + return $error + } + + _getopt_err() { + if [[ $flags != *q* ]]; then + printf '%s\n' "$1" >&2 + fi + } + + _getopt_resolve_abbrev() { + # Resolves an abbrevation from a list of possibilities. + # If the abbreviation is unambiguous, echoes the expansion on stdout + # and returns 0. If the abbreviation is ambiguous, prints a message on + # stderr and returns 1. (For first parse this should convert to exit + # status 2.) If there is no match at all, prints a message on stderr + # and returns 2. + declare a q="$1" + declare -a matches + shift + for a; do + if [[ $q == "$a" ]]; then + # Exact match. Squash any other partial matches. + matches=( "$a" ) + break + elif [[ $flags == *a* && $q == -[^-]* && $a == -"$q" ]]; then + # Exact alternative match. Squash any other partial matches. + matches=( "$a" ) + break + elif [[ $a == "$q"* ]]; then + # Abbreviated match. + matches=( "${matches[@]}" "$a" ) + elif [[ $flags == *a* && $q == -[^-]* && $a == -"$q"* ]]; then + # Abbreviated alternative match. + matches=( "${matches[@]}" "${a#-}" ) + fi + done + case ${#matches[@]} in + (0) + [[ $flags == *q* ]] || \ + printf "$name: unrecognized option %s\\n" >&2 \ + "$(_getopt_quote "$q")" + return 2 ;; + (1) + printf '%s' "${matches[0]}"; return 0 ;; + (*) + [[ $flags == *q* ]] || \ + printf "$name: option %s is ambiguous; possibilities: %s\\n" >&2 \ + "$(_getopt_quote "$q")" "$(_getopt_quote "${matches[@]}")" + return 1 ;; + esac + } + + _getopt_split() { + # Splits $2 at commas to build array specified by $1 + declare IFS=, + eval "$1=( \$2 )" + } + + _getopt_quote() { + # Quotes arguments with single quotes, escaping inner single quotes + declare s space q=\' + for s; do + printf "$space'%s'" "${s//$q/$q\\$q$q}" + space=' ' + done + } + + _getopt_quote_csh() { + # Quotes arguments with single quotes, escaping inner single quotes, + # bangs, backslashes and newlines + declare s i c space + for s; do + echo -n "$space'" + for ((i=0; i<${#s}; i++)); do + c=${s:i:1} + case $c in + (\\|\'|!) + echo -n "'\\$c'" ;; + ($'\n') + echo -n "\\$c" ;; + (*) + echo -n "$c" ;; + esac + done + echo -n \' + space=' ' + done + } + + _getopt_help() { + cat <<-EOT >&2 + Usage: + getopt + getopt [options] [--] + getopt [options] -o|--options [options] [--] + Parse command options. + Options: + -a, --alternative allow long options starting with single - + -l, --longoptions the long options to be recognized + -n, --name the name under which errors are reported + -o, --options the short options to be recognized + -q, --quiet disable error reporting by getopt(3) + -Q, --quiet-output no normal output + -s, --shell set quoting conventions to those of + -T, --test test for getopt(1) version + -u, --unquoted do not quote the output + -h, --help display this help and exit + -V, --version output version information and exit + For more details see getopt(1). + EOT + } + + _getopt_version_check() { + if [[ -z $BASH_VERSION ]]; then + echo "getopt: unknown version of bash might not be compatible" >&2 + return 1 + fi + + # This is a lexical comparison that should be sufficient forever. + if [[ $BASH_VERSION < 2.05b ]]; then + echo "getopt: bash $BASH_VERSION might not be compatible" >&2 + return 1 + fi + + return 0 + } + + _getopt_version_check + _getopt_main "$@" + declare status=$? + unset -f _getopt_main _getopt_err _getopt_parse _getopt_quote \ + _getopt_quote_csh _getopt_resolve_abbrev _getopt_split _getopt_help \ + _getopt_version_check + return $status +} + +[[ $BASH_SOURCE != "$0" ]] || main "$@" From 26ab873aa6fdbb50779e054b85e504386d662224 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Sat, 16 Nov 2019 19:37:56 +0100 Subject: [PATCH 052/214] Updated CHANGELOG --- CHANGELOG.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f74635fd1..33b1549a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,18 @@ ## [Unreleased] -- Changes TFLint hook to not use --deep by default but allow enabling it (along with other parameters) via `args` ([#71](https://github.com/antonbabenko/pre-commit-terraform/issues/71)) + + + +## [v1.21.0] - 2019-11-16 + +- use getopt for args in the tflint hook, following the approach in terraform-docs ([#75](https://github.com/antonbabenko/pre-commit-terraform/issues/75)) + ## [v1.20.0] - 2019-11-02 +- Updated CHANGELOG - Fixes [#65](https://github.com/antonbabenko/pre-commit-terraform/issues/65): terraform-docs should not fail if complex types contain 'description' keyword ([#73](https://github.com/antonbabenko/pre-commit-terraform/issues/73)) - Added FUNDING.yml - Improve installation instructions and make README more readable ([#72](https://github.com/antonbabenko/pre-commit-terraform/issues/72)) @@ -210,7 +217,8 @@ - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.20.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.21.0...HEAD +[v1.21.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.20.0...v1.21.0 [v1.20.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.19.0...v1.20.0 [v1.19.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.18.0...v1.19.0 [v1.18.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.17.0...v1.18.0 From 76969eab80997f4dc0d4eb0cce7f5d497a5826ff Mon Sep 17 00:00:00 2001 From: "Thierno IB. BARRY" Date: Mon, 13 Jan 2020 11:40:35 +0100 Subject: [PATCH 053/214] move terraform-docs args after markdown command (#83) --- terraform_docs.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/terraform_docs.sh b/terraform_docs.sh index 958272eee..490f157c8 100755 --- a/terraform_docs.sh +++ b/terraform_docs.sh @@ -76,7 +76,7 @@ terraform_docs() { fi if [[ "$terraform_docs_awk_file" == "0" ]]; then - terraform-docs $args md ./ > "$tmp_file" + terraform-docs md $args ./ > "$tmp_file" else # Can't append extension for mktemp, so renaming instead tmp_file_docs=$(mktemp "${TMPDIR:-/tmp}/terraform-docs-XXXXXXXXXX") @@ -84,7 +84,7 @@ terraform_docs() { tmp_file_docs_tf="$tmp_file_docs.tf" awk -f "$terraform_docs_awk_file" ./*.tf > "$tmp_file_docs_tf" - terraform-docs $args md "$tmp_file_docs_tf" > "$tmp_file" + terraform-docs md $args "$tmp_file_docs_tf" > "$tmp_file" rm -f "$tmp_file_docs_tf" fi From 9fd71e3934b369e477d4f66543679bdaa1c0d7eb Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Mon, 13 Jan 2020 11:41:09 +0100 Subject: [PATCH 054/214] Updated CHANGELOG --- CHANGELOG.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33b1549a4..4e7a09234 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,16 @@ + +## [v1.22.0] - 2020-01-13 + +- move terraform-docs args after markdown command ([#83](https://github.com/antonbabenko/pre-commit-terraform/issues/83)) + + ## [v1.21.0] - 2019-11-16 +- Updated CHANGELOG - use getopt for args in the tflint hook, following the approach in terraform-docs ([#75](https://github.com/antonbabenko/pre-commit-terraform/issues/75)) @@ -217,7 +224,8 @@ - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.21.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.22.0...HEAD +[v1.22.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.21.0...v1.22.0 [v1.21.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.20.0...v1.21.0 [v1.20.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.19.0...v1.20.0 [v1.19.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.18.0...v1.19.0 From b99e3500e18c3ae145ec519fdba54808318cfbec Mon Sep 17 00:00:00 2001 From: Konstantin Kirpichnikov <56006844+konstantin-recurly@users.noreply.github.com> Date: Tue, 21 Jan 2020 05:19:46 -0500 Subject: [PATCH 055/214] Added support for terraform-docs 0.8.0 with proper support for Terraform 0.12 syntax (bye-bye awk) (#85) --- .pre-commit-config.yaml | 7 +++++- CHANGELOG.md | 1 - README.md | 21 ++++++++--------- terraform_docs.sh | 51 ++++++++++++++++++++++------------------- 4 files changed, 42 insertions(+), 38 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f80cfc805..ba19cfc9f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: git://github.com/pre-commit/pre-commit-hooks - rev: v2.2.3 + rev: v2.4.0 hooks: - id: check-yaml - id: end-of-file-fixer @@ -8,3 +8,8 @@ repos: - id: check-case-conflict - id: check-merge-conflict - id: check-executables-have-shebangs +#- repo: git://github.com/jumanjihouse/pre-commit-hooks +# rev: 1.11.2 +# hooks: +# - id: shellcheck +# - id: shfmt diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e7a09234..f6bd51076 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,6 @@ ## [Unreleased] - ## [v1.22.0] - 2020-01-13 diff --git a/README.md b/README.md index 355ff9711..39f855cbd 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,19 @@ # Collection of git hooks for Terraform to be used with [pre-commit framework](http://pre-commit.com/) -[![Github tag](https://img.shields.io/github/tag/antonbabenko/pre-commit-terraform.svg)](https://github.com/antonbabenko/pre-commit-terraform/releases) ![](https://img.shields.io/maintenance/yes/2019.svg) [![Help Contribute to Open Source](https://www.codetriage.com/antonbabenko/pre-commit-terraform/badges/users.svg)](https://www.codetriage.com/antonbabenko/pre-commit-terraform) +[![Github tag](https://img.shields.io/github/tag/antonbabenko/pre-commit-terraform.svg)](https://github.com/antonbabenko/pre-commit-terraform/releases) ![](https://img.shields.io/maintenance/yes/2020.svg) [![Help Contribute to Open Source](https://www.codetriage.com/antonbabenko/pre-commit-terraform/badges/users.svg)](https://www.codetriage.com/antonbabenko/pre-commit-terraform) ## How to install ### 1. Install dependencies -* [`pre-commit`](http://pre-commit.com/#install) -* [`terraform-docs`](https://github.com/segmentio/terraform-docs) (required for `terraform_docs` hooks) -* GNU `awk` (required for `terraform_docs` hooks in Terraform 0.12) -* [`TFLint`](https://github.com/wata727/tflint) (required for `terraform_tflint` hook) +* [`pre-commit`](https://pre-commit.com/#install) +* [`terraform-docs`](https://github.com/segmentio/terraform-docs) required for `terraform_docs` hooks. `GNU awk` is required if using `terraform-docs` older than 0.8.0 with Terraform 0.12. +* [`TFLint`](https://github.com/terraform-linters/tflint) required for `terraform_tflint` hook. ##### MacOS ```bash -brew install pre-commit awk terraform-docs tflint +brew install pre-commit gawk terraform-docs tflint ``` ##### Ubuntu @@ -23,7 +22,7 @@ brew install pre-commit awk terraform-docs tflint sudo apt install python-pip3 gawk &&\ pip3 install pre-commit curl -L "$(curl -s https://api.github.com/repos/segmentio/terraform-docs/releases/latest | grep -o -E "https://.+?-linux-amd64")" > terraform-docs && chmod +x terraform-docs && sudo mv terraform-docs /usr/bin/ -curl -L "$(curl -s https://api.github.com/repos/wata727/tflint/releases/latest | grep -o -E "https://.+?_linux_amd64.zip")" > tflint.zip && unzip tflint.zip && rm tflint.zip && sudo mv tflint /usr/bin/ +curl -L "$(curl -s https://api.github.com/repos/terraform-linters/tflint/releases/latest | grep -o -E "https://.+?_linux_amd64.zip")" > tflint.zip && unzip tflint.zip && rm tflint.zip && sudo mv tflint /usr/bin/ ``` ### 2. Install the pre-commit hook globally @@ -42,7 +41,7 @@ Step into the repository you want to have the pre-commit hooks installed and run git init cat < .pre-commit-config.yaml - repo: git://github.com/antonbabenko/pre-commit-terraform - rev: v1.19.0 + rev: # Get the latest from: https://github.com/antonbabenko/pre-commit-terraform/releases hooks: - id: terraform_fmt - id: terraform_docs @@ -59,7 +58,7 @@ pre-commit run -a ## Available Hooks -There are several [pre-commit](http://pre-commit.com/) hooks to keep Terraform configurations (both `*.tf` and `*.tfvars`) and Terragrunt configurations (`*.hcl`) in a good shape: +There are several [pre-commit](https://pre-commit.com/) hooks to keep Terraform configurations (both `*.tf` and `*.tfvars`) and Terragrunt configurations (`*.hcl`) in a good shape: | Hook name | Description | | ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- | @@ -68,7 +67,7 @@ There are several [pre-commit](http://pre-commit.com/) hooks to keep Terraform c | `terraform_docs` | Inserts input and output documentation into `README.md`. Recommended. | | `terraform_docs_without_aggregate_type_defaults` | Inserts input and output documentation into `README.md` without aggregate type defaults. | | `terraform_docs_replace` | Runs `terraform-docs` and pipes the output directly to README.md | -| `terraform_tflint` | Validates all Terraform configuration files with [TFLint](https://github.com/wata727/tflint). | +| `terraform_tflint` | Validates all Terraform configuration files with [TFLint](https://github.com/terraform-linters/tflint). | | `terragrunt_fmt` | Rewrites all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) to a canonical format. | Check the [source file](https://github.com/antonbabenko/pre-commit-terraform/blob/master/.pre-commit-hooks.yaml) to know arguments used for each hook. @@ -94,8 +93,6 @@ if they are present in `README.md`. 1. It is possible to pass additional arguments to shell scripts when using `terraform_docs` and `terraform_docs_without_aggregate_type_defaults`. Send pull-request with the new hook if there is something missing. -1. `terraform-docs` works with Terraform 0.12 but support is hackish (it requires `awk` to be installed) and may contain bugs. You can follow the native support of Terraform 0.12 in `terraform-docs` in [issue #62](https://github.com/segmentio/terraform-docs/issues/62). - ## Notes about terraform_tflint hooks 1. `terraform_tflint` supports custom arguments so you can enable module inspection, deep check mode etc. diff --git a/terraform_docs.sh b/terraform_docs.sh index 490f157c8..60a09a8c9 100755 --- a/terraform_docs.sh +++ b/terraform_docs.sh @@ -25,19 +25,38 @@ main() { esac done - local hack_terraform_docs=$(terraform version | head -1 | grep -c 0.12) + local hack_terraform_docs + hack_terraform_docs=$(terraform version | head -1 | grep -c 0.12) - if [[ "$hack_terraform_docs" == "1" ]]; then - which awk 2>&1 >/dev/null || ( echo "awk is required for terraform-docs hack to work with Terraform 0.12"; exit 1) + 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) + + if [[ -z "$is_old_terraform_docs" ]]; then # Using terraform-docs 0.8+ (preferred) + + terraform_docs "0" "$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 tmp_file_awk=$(mktemp "${TMPDIR:-/tmp}/terraform-docs-XXXXXXXXXX") terraform_docs_awk "$tmp_file_awk" terraform_docs "$tmp_file_awk" "$args" "$files" rm -f "$tmp_file_awk" - else + + else # Using terraform 0.11 and no awk script is needed for that + terraform_docs "0" "$args" "$files" - fi + fi } terraform_docs() { @@ -76,7 +95,7 @@ terraform_docs() { fi if [[ "$terraform_docs_awk_file" == "0" ]]; then - terraform-docs md $args ./ > "$tmp_file" + terraform-docs md "$args" ./ > "$tmp_file" else # Can't append extension for mktemp, so renaming instead tmp_file_docs=$(mktemp "${TMPDIR:-/tmp}/terraform-docs-XXXXXXXXXX") @@ -84,7 +103,7 @@ terraform_docs() { tmp_file_docs_tf="$tmp_file_docs.tf" awk -f "$terraform_docs_awk_file" ./*.tf > "$tmp_file_docs_tf" - terraform-docs md $args "$tmp_file_docs_tf" > "$tmp_file" + terraform-docs md "$args" "$tmp_file_docs_tf" > "$tmp_file" rm -f "$tmp_file_docs_tf" fi @@ -103,24 +122,19 @@ terraform_docs() { terraform_docs_awk() { readonly output_file=$1 - cat <<"EOF" > $output_file + 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/segmentio/terraform-docs/ # https://github.com/segmentio/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 "..." { # ---------------------------------------------------------------------------------------------- @@ -142,8 +156,6 @@ terraform_docs_awk() { # Print variable|output line print $0 } - - # ---------------------------------------------------------------------------------------------- # default = ... # ---------------------------------------------------------------------------------------------- @@ -177,8 +189,6 @@ terraform_docs_awk() { } } } - - # ---------------------------------------------------------------------------------------------- # type = ... # ---------------------------------------------------------------------------------------------- @@ -211,7 +221,6 @@ terraform_docs_awk() { } else { type = $3 } - # legacy quoted types: "string", "list", and "map" if (type ~ /^[[:space:]]*"(.*?)"[[:space:]]*$/) { print " type = " type @@ -229,8 +238,6 @@ terraform_docs_awk() { blockTypeCnt -= gsub(/\}/, "") } } - - # ---------------------------------------------------------------------------------------------- # description = ... # ---------------------------------------------------------------------------------------------- @@ -240,8 +247,6 @@ terraform_docs_awk() { print $0 } } - - # ---------------------------------------------------------------------------------------------- # value = ... # ---------------------------------------------------------------------------------------------- @@ -251,8 +256,6 @@ terraform_docs_awk() { # print $0 # } #} - - # ---------------------------------------------------------------------------------------------- # Newlines, comments, everything else # ---------------------------------------------------------------------------------------------- From 07730a425a6798be7d92460dc204fd1f2015ef10 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Tue, 21 Jan 2020 11:20:16 +0100 Subject: [PATCH 056/214] Updated CHANGELOG --- CHANGELOG.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f6bd51076..3ea63feb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,17 @@ ## [Unreleased] + + +## [v1.23.0] - 2020-01-21 + +- Added support for terraform-docs 0.8.0 with proper support for Terraform 0.12 syntax (bye-bye awk) ([#85](https://github.com/antonbabenko/pre-commit-terraform/issues/85)) + + ## [v1.22.0] - 2020-01-13 +- Updated CHANGELOG - move terraform-docs args after markdown command ([#83](https://github.com/antonbabenko/pre-commit-terraform/issues/83)) @@ -223,7 +231,8 @@ - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.22.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.23.0...HEAD +[v1.23.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.22.0...v1.23.0 [v1.22.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.21.0...v1.22.0 [v1.21.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.20.0...v1.21.0 [v1.20.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.19.0...v1.20.0 From 1e5b3af0d2234f8b5ff6d1da091ccfe87deacf30 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Tue, 21 Jan 2020 11:54:13 +0100 Subject: [PATCH 057/214] Added shfmt to autoformat shell scripts (#86) --- .pre-commit-config.yaml | 10 +- terraform_docs.sh | 246 ++++++++++++++++++++++------------------ terraform_tflint.sh | 216 ++++++++++++++++++++--------------- terraform_validate.sh | 6 +- 4 files changed, 269 insertions(+), 209 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ba19cfc9f..ad93b0cc7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,8 +8,8 @@ repos: - id: check-case-conflict - id: check-merge-conflict - id: check-executables-have-shebangs -#- repo: git://github.com/jumanjihouse/pre-commit-hooks -# rev: 1.11.2 -# hooks: -# - id: shellcheck -# - id: shfmt +- repo: git://github.com/jumanjihouse/pre-commit-hooks + rev: 1.11.2 + hooks: + - id: shfmt + args: ['-l', '-i', '2', '-ci', '-sr', '-w'] diff --git a/terraform_docs.sh b/terraform_docs.sh index 60a09a8c9..6a54454a6 100755 --- a/terraform_docs.sh +++ b/terraform_docs.sh @@ -12,12 +12,12 @@ main() { for argv; do case $argv in - (-a|--args) + -a | --args) shift args="$1" shift ;; - (--) + --) shift files="$@" break @@ -36,7 +36,7 @@ main() { local is_old_terraform_docs is_old_terraform_docs=$(terraform-docs version | grep -o "v0.[1-7]" | tail -1) - if [[ -z "$is_old_terraform_docs" ]]; then # Using terraform-docs 0.8+ (preferred) + if [[ -z "$is_old_terraform_docs" ]]; then # Using terraform-docs 0.8+ (preferred) terraform_docs "0" "$args" "$files" @@ -78,7 +78,7 @@ terraform_docs() { tfvars_files+=("$file_with_path") fi - ((index+=1)) + ((index += 1)) done readonly tmp_file=$(mktemp) @@ -94,18 +94,18 @@ terraform_docs() { continue fi - if [[ "$terraform_docs_awk_file" == "0" ]]; then - terraform-docs md "$args" ./ > "$tmp_file" - else - # Can't append extension for mktemp, so renaming instead - tmp_file_docs=$(mktemp "${TMPDIR:-/tmp}/terraform-docs-XXXXXXXXXX") - mv "$tmp_file_docs" "$tmp_file_docs.tf" - tmp_file_docs_tf="$tmp_file_docs.tf" - - awk -f "$terraform_docs_awk_file" ./*.tf > "$tmp_file_docs_tf" - terraform-docs md "$args" "$tmp_file_docs_tf" > "$tmp_file" - rm -f "$tmp_file_docs_tf" - fi + if [[ "$terraform_docs_awk_file" == "0" ]]; then + terraform-docs md "$args" ./ > "$tmp_file" + else + # Can't append extension for mktemp, so renaming instead + tmp_file_docs=$(mktemp "${TMPDIR:-/tmp}/terraform-docs-XXXXXXXXXX") + mv "$tmp_file_docs" "$tmp_file_docs.tf" + tmp_file_docs_tf="$tmp_file_docs.tf" + + awk -f "$terraform_docs_awk_file" ./*.tf > "$tmp_file_docs_tf" + terraform-docs md "$args" "$tmp_file_docs_tf" > "$tmp_file" + rm -f "$tmp_file_docs_tf" + fi # 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" @@ -122,7 +122,7 @@ terraform_docs() { terraform_docs_awk() { readonly output_file=$1 - cat <<"EOF" > "$output_file" + 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/segmentio/terraform-docs/ @@ -353,59 +353,73 @@ getopt() { while [[ $# -gt 0 ]]; do case $1 in - (-a|--alternative) - flags=a$flags ;; + -a | --alternative) + flags=a$flags + ;; - (-h|--help) + -h | --help) _getopt_help - return 2 # as does GNU getopt + return 2 # as does GNU getopt ;; - (-l|--longoptions) + -l | --longoptions) long="$long${long:+,}$2" - shift ;; + shift + ;; - (-n|--name) + -n | --name) name=$2 - shift ;; + shift + ;; - (-o|--options) + -o | --options) short=$2 have_short=true - shift ;; + shift + ;; - (-q|--quiet) - flags=q$flags ;; + -q | --quiet) + flags=q$flags + ;; - (-Q|--quiet-output) - flags=Q$flags ;; + -Q | --quiet-output) + flags=Q$flags + ;; - (-s|--shell) + -s | --shell) case $2 in - (sh|bash) - flags=${flags//t/} ;; - (csh|tcsh) - flags=t$flags ;; - (*) + sh | bash) + flags=${flags//t/} + ;; + csh | tcsh) + flags=t$flags + ;; + *) echo 'getopt: unknown shell after -s or --shell argument' >&2 echo "Try \`getopt --help' for more information." >&2 - return 2 ;; + return 2 + ;; esac - shift ;; + shift + ;; - (-u|--unquoted) - flags=u$flags ;; + -u | --unquoted) + flags=u$flags + ;; - (-T|--test) - return 4 ;; + -T | --test) + return 4 + ;; - (-V|--version) + -V | --version) echo "pure-getopt 1.4.3" - return 0 ;; + return 0 + ;; - (--) + --) shift - break ;; + break + ;; esac shift @@ -466,9 +480,9 @@ getopt() { # for use with _getopt_resolve_abbrev declare -a longarr _getopt_split longarr "$long" - longarr=( "${longarr[@]/#/--}" ) - longarr=( "${longarr[@]%:}" ) - longarr=( "${longarr[@]%:}" ) + longarr=("${longarr[@]/#/--}") + longarr=("${longarr[@]%:}") + longarr=("${longarr[@]%:}") # Parse and collect options and parameters declare -a opts params @@ -476,18 +490,19 @@ getopt() { while [[ $# -gt 0 ]]; do case $1 in - (--) - params=( "${params[@]}" "${@:2}" ) - break ;; + --) + params=("${params[@]}" "${@:2}") + break + ;; - (--*=*) + --*=*) o=${1%%=*} if ! o=$(_getopt_resolve_abbrev "$o" "${longarr[@]}"); then error=1 elif [[ ,"$long", == *,"${o#--}"::,* ]]; then - opts=( "${opts[@]}" "$o" "${1#*=}" ) + opts=("${opts[@]}" "$o" "${1#*=}") elif [[ ,"$long", == *,"${o#--}":,* ]]; then - opts=( "${opts[@]}" "$o" "${1#*=}" ) + opts=("${opts[@]}" "$o" "${1#*=}") elif [[ ,"$long", == *,"${o#--}",* ]]; then if $alt_recycled; then o=${o#-}; fi _getopt_err "$name: option '$o' doesn't allow an argument" @@ -499,18 +514,18 @@ getopt() { alt_recycled=false ;; - (--?*) + --?*) o=$1 if ! o=$(_getopt_resolve_abbrev "$o" "${longarr[@]}"); then error=1 elif [[ ,"$long", == *,"${o#--}",* ]]; then - opts=( "${opts[@]}" "$o" ) + opts=("${opts[@]}" "$o") elif [[ ,"$long", == *,"${o#--}::",* ]]; then - opts=( "${opts[@]}" "$o" '' ) + opts=("${opts[@]}" "$o" '') elif [[ ,"$long", == *,"${o#--}:",* ]]; then if [[ $# -ge 2 ]]; then shift - opts=( "${opts[@]}" "$o" "$1" ) + opts=("${opts[@]}" "$o" "$1") else if $alt_recycled; then o=${o#-}; fi _getopt_err "$name: option '$o' requires an argument" @@ -523,7 +538,7 @@ getopt() { alt_recycled=false ;; - (-*) + -*) if [[ $flags == *a* ]]; then # Alternative parsing mode! # Try to handle as a long option if any of the following apply: @@ -533,28 +548,32 @@ getopt() { # 4. There's a single letter and no short match o=${1::2} # temp for testing #4 if [[ $1 == *=* || $1 == -?? || \ - ,$long, == *,"${1#-}"[:,]* || \ - ,$short, != *,"${o#-}"[:,]* ]]; then - o=$(_getopt_resolve_abbrev "${1%%=*}" "${longarr[@]}" 2>/dev/null) + ,$long, == *,"${1#-}"[:,]* || \ + ,$short, != *,"${o#-}"[:,]* ]]; then + o=$(_getopt_resolve_abbrev "${1%%=*}" "${longarr[@]}" 2> /dev/null) case $? in - (0) + 0) # Unambiguous match. Let the long options parser handle # it, with a flag to get the right error message. set -- "-$1" "${@:2}" alt_recycled=true - continue ;; - (1) + continue + ;; + 1) # Ambiguous match, generate error and continue. - _getopt_resolve_abbrev "${1%%=*}" "${longarr[@]}" >/dev/null + _getopt_resolve_abbrev "${1%%=*}" "${longarr[@]}" > /dev/null error=1 shift - continue ;; - (2) + continue + ;; + 2) # No match, fall through to single-character check. - true ;; - (*) + true + ;; + *) echo "getopt: assertion failed (3)" >&2 - return 3 ;; + return 3 + ;; esac fi fi @@ -562,22 +581,22 @@ getopt() { o=${1::2} if [[ "$short" == *"${o#-}"::* ]]; then if [[ ${#1} -gt 2 ]]; then - opts=( "${opts[@]}" "$o" "${1:2}" ) + opts=("${opts[@]}" "$o" "${1:2}") else - opts=( "${opts[@]}" "$o" '' ) + opts=("${opts[@]}" "$o" '') fi elif [[ "$short" == *"${o#-}":* ]]; then if [[ ${#1} -gt 2 ]]; then - opts=( "${opts[@]}" "$o" "${1:2}" ) + opts=("${opts[@]}" "$o" "${1:2}") elif [[ $# -ge 2 ]]; then shift - opts=( "${opts[@]}" "$o" "$1" ) + opts=("${opts[@]}" "$o" "$1") else _getopt_err "$name: option requires an argument -- '${o#-}'" error=1 fi elif [[ "$short" == *"${o#-}"* ]]; then - opts=( "${opts[@]}" "$o" ) + opts=("${opts[@]}" "$o") if [[ ${#1} -gt 2 ]]; then set -- "$o" "-${1:2}" "${@:2}" fi @@ -594,26 +613,28 @@ getopt() { fi fi error=1 - fi ;; + fi + ;; - (*) + *) # GNU getopt in-place mode (leading dash on short options) # overrides POSIXLY_CORRECT if [[ $flags == *i* ]]; then - opts=( "${opts[@]}" "$1" ) + opts=("${opts[@]}" "$1") elif [[ $flags == *p* ]]; then - params=( "${params[@]}" "$@" ) + params=("${params[@]}" "$@") break else - params=( "${params[@]}" "$1" ) + params=("${params[@]}" "$1") fi + ;; esac shift done if [[ $flags == *Q* ]]; then - true # generate no output + true # generate no output else echo -n ' ' if [[ $flags == *[cu]* ]]; then @@ -650,33 +671,39 @@ getopt() { for a; do if [[ $q == "$a" ]]; then # Exact match. Squash any other partial matches. - matches=( "$a" ) + matches=("$a") break elif [[ $flags == *a* && $q == -[^-]* && $a == -"$q" ]]; then # Exact alternative match. Squash any other partial matches. - matches=( "$a" ) + matches=("$a") break elif [[ $a == "$q"* ]]; then # Abbreviated match. - matches=( "${matches[@]}" "$a" ) + matches=("${matches[@]}" "$a") elif [[ $flags == *a* && $q == -[^-]* && $a == -"$q"* ]]; then # Abbreviated alternative match. - matches=( "${matches[@]}" "${a#-}" ) + matches=("${matches[@]}" "${a#-}") fi done case ${#matches[@]} in - (0) - [[ $flags == *q* ]] || \ - printf "$name: unrecognized option %s\\n" >&2 \ - "$(_getopt_quote "$q")" - return 2 ;; - (1) - printf '%s' "${matches[0]}"; return 0 ;; - (*) - [[ $flags == *q* ]] || \ - printf "$name: option %s is ambiguous; possibilities: %s\\n" >&2 \ - "$(_getopt_quote "$q")" "$(_getopt_quote "${matches[@]}")" - return 1 ;; + 0) + [[ $flags == *q* ]] || + printf "$name: unrecognized option %s\\n" \ + "$(_getopt_quote "$q")" >&2 + + return 2 + ;; + 1) + printf '%s' "${matches[0]}" + return 0 + ;; + *) + [[ $flags == *q* ]] || + printf "$name: option %s is ambiguous; possibilities: %s\\n" \ + "$(_getopt_quote "$q")" "$(_getopt_quote "${matches[@]}")" >&2 + + return 1 + ;; esac } @@ -701,15 +728,18 @@ getopt() { declare s i c space for s; do echo -n "$space'" - for ((i=0; i<${#s}; i++)); do + for ((i = 0; i < ${#s}; i++)); do c=${s:i:1} case $c in - (\\|\'|!) - echo -n "'\\$c'" ;; - ($'\n') - echo -n "\\$c" ;; - (*) - echo -n "$c" ;; + \\ | \' | !) + echo -n "'\\$c'" + ;; + $'\n') + echo -n "\\$c" + ;; + *) + echo -n "$c" + ;; esac done echo -n \' @@ -718,7 +748,7 @@ getopt() { } _getopt_help() { - cat <<-EOT >&2 + cat <<- EOT >&2 Usage: getopt diff --git a/terraform_tflint.sh b/terraform_tflint.sh index 2393c3836..eb77d1c8c 100755 --- a/terraform_tflint.sh +++ b/terraform_tflint.sh @@ -11,12 +11,12 @@ main() { for argv; do case $argv in - (-a|--args) + -a | --args) shift args="$1" shift ;; - (--) + --) shift files="$@" break @@ -120,59 +120,73 @@ getopt() { while [[ $# -gt 0 ]]; do case $1 in - (-a|--alternative) - flags=a$flags ;; + -a | --alternative) + flags=a$flags + ;; - (-h|--help) + -h | --help) _getopt_help - return 2 # as does GNU getopt + return 2 # as does GNU getopt ;; - (-l|--longoptions) + -l | --longoptions) long="$long${long:+,}$2" - shift ;; + shift + ;; - (-n|--name) + -n | --name) name=$2 - shift ;; + shift + ;; - (-o|--options) + -o | --options) short=$2 have_short=true - shift ;; + shift + ;; - (-q|--quiet) - flags=q$flags ;; + -q | --quiet) + flags=q$flags + ;; - (-Q|--quiet-output) - flags=Q$flags ;; + -Q | --quiet-output) + flags=Q$flags + ;; - (-s|--shell) + -s | --shell) case $2 in - (sh|bash) - flags=${flags//t/} ;; - (csh|tcsh) - flags=t$flags ;; - (*) + sh | bash) + flags=${flags//t/} + ;; + csh | tcsh) + flags=t$flags + ;; + *) echo 'getopt: unknown shell after -s or --shell argument' >&2 echo "Try \`getopt --help' for more information." >&2 - return 2 ;; + return 2 + ;; esac - shift ;; + shift + ;; - (-u|--unquoted) - flags=u$flags ;; + -u | --unquoted) + flags=u$flags + ;; - (-T|--test) - return 4 ;; + -T | --test) + return 4 + ;; - (-V|--version) + -V | --version) echo "pure-getopt 1.4.3" - return 0 ;; + return 0 + ;; - (--) + --) shift - break ;; + break + ;; esac shift @@ -233,9 +247,9 @@ getopt() { # for use with _getopt_resolve_abbrev declare -a longarr _getopt_split longarr "$long" - longarr=( "${longarr[@]/#/--}" ) - longarr=( "${longarr[@]%:}" ) - longarr=( "${longarr[@]%:}" ) + longarr=("${longarr[@]/#/--}") + longarr=("${longarr[@]%:}") + longarr=("${longarr[@]%:}") # Parse and collect options and parameters declare -a opts params @@ -243,18 +257,19 @@ getopt() { while [[ $# -gt 0 ]]; do case $1 in - (--) - params=( "${params[@]}" "${@:2}" ) - break ;; + --) + params=("${params[@]}" "${@:2}") + break + ;; - (--*=*) + --*=*) o=${1%%=*} if ! o=$(_getopt_resolve_abbrev "$o" "${longarr[@]}"); then error=1 elif [[ ,"$long", == *,"${o#--}"::,* ]]; then - opts=( "${opts[@]}" "$o" "${1#*=}" ) + opts=("${opts[@]}" "$o" "${1#*=}") elif [[ ,"$long", == *,"${o#--}":,* ]]; then - opts=( "${opts[@]}" "$o" "${1#*=}" ) + opts=("${opts[@]}" "$o" "${1#*=}") elif [[ ,"$long", == *,"${o#--}",* ]]; then if $alt_recycled; then o=${o#-}; fi _getopt_err "$name: option '$o' doesn't allow an argument" @@ -266,18 +281,18 @@ getopt() { alt_recycled=false ;; - (--?*) + --?*) o=$1 if ! o=$(_getopt_resolve_abbrev "$o" "${longarr[@]}"); then error=1 elif [[ ,"$long", == *,"${o#--}",* ]]; then - opts=( "${opts[@]}" "$o" ) + opts=("${opts[@]}" "$o") elif [[ ,"$long", == *,"${o#--}::",* ]]; then - opts=( "${opts[@]}" "$o" '' ) + opts=("${opts[@]}" "$o" '') elif [[ ,"$long", == *,"${o#--}:",* ]]; then if [[ $# -ge 2 ]]; then shift - opts=( "${opts[@]}" "$o" "$1" ) + opts=("${opts[@]}" "$o" "$1") else if $alt_recycled; then o=${o#-}; fi _getopt_err "$name: option '$o' requires an argument" @@ -290,7 +305,7 @@ getopt() { alt_recycled=false ;; - (-*) + -*) if [[ $flags == *a* ]]; then # Alternative parsing mode! # Try to handle as a long option if any of the following apply: @@ -300,28 +315,32 @@ getopt() { # 4. There's a single letter and no short match o=${1::2} # temp for testing #4 if [[ $1 == *=* || $1 == -?? || \ - ,$long, == *,"${1#-}"[:,]* || \ - ,$short, != *,"${o#-}"[:,]* ]]; then - o=$(_getopt_resolve_abbrev "${1%%=*}" "${longarr[@]}" 2>/dev/null) + ,$long, == *,"${1#-}"[:,]* || \ + ,$short, != *,"${o#-}"[:,]* ]]; then + o=$(_getopt_resolve_abbrev "${1%%=*}" "${longarr[@]}" 2> /dev/null) case $? in - (0) + 0) # Unambiguous match. Let the long options parser handle # it, with a flag to get the right error message. set -- "-$1" "${@:2}" alt_recycled=true - continue ;; - (1) + continue + ;; + 1) # Ambiguous match, generate error and continue. - _getopt_resolve_abbrev "${1%%=*}" "${longarr[@]}" >/dev/null + _getopt_resolve_abbrev "${1%%=*}" "${longarr[@]}" > /dev/null error=1 shift - continue ;; - (2) + continue + ;; + 2) # No match, fall through to single-character check. - true ;; - (*) + true + ;; + *) echo "getopt: assertion failed (3)" >&2 - return 3 ;; + return 3 + ;; esac fi fi @@ -329,22 +348,22 @@ getopt() { o=${1::2} if [[ "$short" == *"${o#-}"::* ]]; then if [[ ${#1} -gt 2 ]]; then - opts=( "${opts[@]}" "$o" "${1:2}" ) + opts=("${opts[@]}" "$o" "${1:2}") else - opts=( "${opts[@]}" "$o" '' ) + opts=("${opts[@]}" "$o" '') fi elif [[ "$short" == *"${o#-}":* ]]; then if [[ ${#1} -gt 2 ]]; then - opts=( "${opts[@]}" "$o" "${1:2}" ) + opts=("${opts[@]}" "$o" "${1:2}") elif [[ $# -ge 2 ]]; then shift - opts=( "${opts[@]}" "$o" "$1" ) + opts=("${opts[@]}" "$o" "$1") else _getopt_err "$name: option requires an argument -- '${o#-}'" error=1 fi elif [[ "$short" == *"${o#-}"* ]]; then - opts=( "${opts[@]}" "$o" ) + opts=("${opts[@]}" "$o") if [[ ${#1} -gt 2 ]]; then set -- "$o" "-${1:2}" "${@:2}" fi @@ -361,26 +380,28 @@ getopt() { fi fi error=1 - fi ;; + fi + ;; - (*) + *) # GNU getopt in-place mode (leading dash on short options) # overrides POSIXLY_CORRECT if [[ $flags == *i* ]]; then - opts=( "${opts[@]}" "$1" ) + opts=("${opts[@]}" "$1") elif [[ $flags == *p* ]]; then - params=( "${params[@]}" "$@" ) + params=("${params[@]}" "$@") break else - params=( "${params[@]}" "$1" ) + params=("${params[@]}" "$1") fi + ;; esac shift done if [[ $flags == *Q* ]]; then - true # generate no output + true # generate no output else echo -n ' ' if [[ $flags == *[cu]* ]]; then @@ -417,33 +438,39 @@ getopt() { for a; do if [[ $q == "$a" ]]; then # Exact match. Squash any other partial matches. - matches=( "$a" ) + matches=("$a") break elif [[ $flags == *a* && $q == -[^-]* && $a == -"$q" ]]; then # Exact alternative match. Squash any other partial matches. - matches=( "$a" ) + matches=("$a") break elif [[ $a == "$q"* ]]; then # Abbreviated match. - matches=( "${matches[@]}" "$a" ) + matches=("${matches[@]}" "$a") elif [[ $flags == *a* && $q == -[^-]* && $a == -"$q"* ]]; then # Abbreviated alternative match. - matches=( "${matches[@]}" "${a#-}" ) + matches=("${matches[@]}" "${a#-}") fi done case ${#matches[@]} in - (0) - [[ $flags == *q* ]] || \ - printf "$name: unrecognized option %s\\n" >&2 \ - "$(_getopt_quote "$q")" - return 2 ;; - (1) - printf '%s' "${matches[0]}"; return 0 ;; - (*) - [[ $flags == *q* ]] || \ - printf "$name: option %s is ambiguous; possibilities: %s\\n" >&2 \ - "$(_getopt_quote "$q")" "$(_getopt_quote "${matches[@]}")" - return 1 ;; + 0) + [[ $flags == *q* ]] || + printf "$name: unrecognized option %s\\n" \ + "$(_getopt_quote "$q")" >&2 + + return 2 + ;; + 1) + printf '%s' "${matches[0]}" + return 0 + ;; + *) + [[ $flags == *q* ]] || + printf "$name: option %s is ambiguous; possibilities: %s\\n" \ + "$(_getopt_quote "$q")" "$(_getopt_quote "${matches[@]}")" >&2 + + return 1 + ;; esac } @@ -468,15 +495,18 @@ getopt() { declare s i c space for s; do echo -n "$space'" - for ((i=0; i<${#s}; i++)); do + for ((i = 0; i < ${#s}; i++)); do c=${s:i:1} case $c in - (\\|\'|!) - echo -n "'\\$c'" ;; - ($'\n') - echo -n "\\$c" ;; - (*) - echo -n "$c" ;; + \\ | \' | !) + echo -n "'\\$c'" + ;; + $'\n') + echo -n "\\$c" + ;; + *) + echo -n "$c" + ;; esac done echo -n \' @@ -485,7 +515,7 @@ getopt() { } _getopt_help() { - cat <<-EOT >&2 + cat <<- EOT >&2 Usage: getopt getopt [options] [--] diff --git a/terraform_validate.sh b/terraform_validate.sh index 6f7eccac2..50bfd0ba7 100755 --- a/terraform_validate.sh +++ b/terraform_validate.sh @@ -9,13 +9,13 @@ for file_with_path in "$@"; do file_with_path="${file_with_path// /__REPLACED__SPACE__}" paths[index]=$(dirname "$file_with_path") - (( "index+=1" )) + (("index+=1")) done for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do path_uniq="${path_uniq//__REPLACED__SPACE__/ }" - if [[ -n "$(find . -maxdepth 1 -name '*.tf' -print -quit)" ]] ; then + if [[ -n "$(find . -maxdepth 1 -name '*.tf' -print -quit)" ]]; then if ! terraform validate $path_uniq; then error=1 echo @@ -25,6 +25,6 @@ for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do fi done -if [[ "${error}" -ne 0 ]] ; then +if [[ "${error}" -ne 0 ]]; then exit 1 fi From 47346044cb78f63a6c2e6de339fc2caa34960ca2 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Tue, 21 Jan 2020 11:54:31 +0100 Subject: [PATCH 058/214] Updated CHANGELOG --- CHANGELOG.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ea63feb0..450474ebc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,16 @@ + +## [v1.24.0] - 2020-01-21 + +- Added shfmt to autoformat shell scripts ([#86](https://github.com/antonbabenko/pre-commit-terraform/issues/86)) + + ## [v1.23.0] - 2020-01-21 +- Updated CHANGELOG - Added support for terraform-docs 0.8.0 with proper support for Terraform 0.12 syntax (bye-bye awk) ([#85](https://github.com/antonbabenko/pre-commit-terraform/issues/85)) @@ -231,7 +238,8 @@ - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.23.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.24.0...HEAD +[v1.24.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.23.0...v1.24.0 [v1.23.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.22.0...v1.23.0 [v1.22.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.21.0...v1.22.0 [v1.21.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.20.0...v1.21.0 From e990429660acabe17a55f6b64d91ea3b4f331abd Mon Sep 17 00:00:00 2001 From: Robson Roberto Souza Peixoto <124390+robsonpeixoto@users.noreply.github.com> Date: Thu, 30 Jan 2020 07:18:01 -0300 Subject: [PATCH 059/214] Fixed tflint hook to iterate over files (#77) --- terraform_tflint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform_tflint.sh b/terraform_tflint.sh index eb77d1c8c..b8fcd8f0e 100755 --- a/terraform_tflint.sh +++ b/terraform_tflint.sh @@ -28,7 +28,7 @@ main() { } tflint_() { - for file_with_path in "$@"; do + for file_with_path in $files; do file_with_path="${file_with_path// /__REPLACED__SPACE__}" paths[index]=$(dirname "$file_with_path") From 7f7ce9eebba4b265121718cb1c5b619e40f168b0 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Thu, 30 Jan 2020 11:18:50 +0100 Subject: [PATCH 060/214] Updated CHANGELOG --- CHANGELOG.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 450474ebc..9e16616ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,16 @@ + +## [v1.25.0] - 2020-01-30 + +- Fixed tflint hook to iterate over files ([#77](https://github.com/antonbabenko/pre-commit-terraform/issues/77)) + + ## [v1.24.0] - 2020-01-21 +- Updated CHANGELOG - Added shfmt to autoformat shell scripts ([#86](https://github.com/antonbabenko/pre-commit-terraform/issues/86)) @@ -238,7 +245,8 @@ - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.24.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.25.0...HEAD +[v1.25.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.24.0...v1.25.0 [v1.24.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.23.0...v1.24.0 [v1.23.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.22.0...v1.23.0 [v1.22.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.21.0...v1.22.0 From 2ebb28ad16f35bac03021a0eb229d40cbdc6a2cb Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Fri, 21 Feb 2020 13:47:04 +0100 Subject: [PATCH 061/214] Fixed exit code for terraform 0.11 branch in terraform_docs (#94) --- terraform_docs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform_docs.sh b/terraform_docs.sh index 6a54454a6..a03f12a16 100755 --- a/terraform_docs.sh +++ b/terraform_docs.sh @@ -26,7 +26,7 @@ main() { done local hack_terraform_docs - hack_terraform_docs=$(terraform version | head -1 | grep -c 0.12) + hack_terraform_docs=$(terraform version | head -1 | grep -c 0.12) || true 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." From 59e48e4f04e8bdb93e27edc2fe08f8c96c632735 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Fri, 21 Feb 2020 13:47:52 +0100 Subject: [PATCH 062/214] Updated pre-commit-hooks --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ad93b0cc7..460616c1e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: git://github.com/pre-commit/pre-commit-hooks - rev: v2.4.0 + rev: v2.5.0 hooks: - id: check-yaml - id: end-of-file-fixer From e321ff1b9cdf2ed8dcb6ceb7a26a5accc40abb3a Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Fri, 21 Feb 2020 13:48:05 +0100 Subject: [PATCH 063/214] Updated CHANGELOG --- CHANGELOG.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e16616ad..e7567f0fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,17 @@ + +## [v1.26.0] - 2020-02-21 + +- Updated pre-commit-hooks +- Fixed exit code for terraform 0.11 branch in terraform_docs ([#94](https://github.com/antonbabenko/pre-commit-terraform/issues/94)) + + ## [v1.25.0] - 2020-01-30 +- Updated CHANGELOG - Fixed tflint hook to iterate over files ([#77](https://github.com/antonbabenko/pre-commit-terraform/issues/77)) @@ -245,7 +253,8 @@ - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.25.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.26.0...HEAD +[v1.26.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.25.0...v1.26.0 [v1.25.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.24.0...v1.25.0 [v1.24.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.23.0...v1.24.0 [v1.23.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.22.0...v1.23.0 From e402c03b6a7275cf3320d732ae792a0c4b61cda9 Mon Sep 17 00:00:00 2001 From: Martin Coxall Date: Mon, 2 Mar 2020 14:48:53 +0000 Subject: [PATCH 064/214] corrected tflint documentation (#95) --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 39f855cbd..386451ab8 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,15 @@ if they are present in `README.md`. ```yaml hooks: - id: terraform_tflint - args: ['--deep'] + args: ['args=--deep'] + ``` + + In order to pass multiple args, try the following: + ```yaml + - id: terraform_tflint + args: + - 'args=--deep' + - 'args=--enable-rule=terraform_documented_variables' ``` ## Notes for developers From 0a75b5bafe3068ecc7d1f64c71dbc1ecfcc98019 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Mon, 2 Mar 2020 15:49:27 +0100 Subject: [PATCH 065/214] Updated CHANGELOG --- CHANGELOG.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7567f0fe..6cc48a41a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,16 @@ + +## [v1.27.0] - 2020-03-02 + +- corrected tflint documentation ([#95](https://github.com/antonbabenko/pre-commit-terraform/issues/95)) + + ## [v1.26.0] - 2020-02-21 +- Updated CHANGELOG - Updated pre-commit-hooks - Fixed exit code for terraform 0.11 branch in terraform_docs ([#94](https://github.com/antonbabenko/pre-commit-terraform/issues/94)) @@ -253,7 +260,8 @@ - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.26.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.27.0...HEAD +[v1.27.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.26.0...v1.27.0 [v1.26.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.25.0...v1.26.0 [v1.25.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.24.0...v1.25.0 [v1.24.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.23.0...v1.24.0 From 800756d8950ce3dfccd76adc71afb86119bf8690 Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Wed, 18 Mar 2020 16:33:35 +0200 Subject: [PATCH 066/214] Update installation instructions (#79) - Fix package name misspell - TFlint migrate to another organization --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 386451ab8..3dd71204a 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ brew install pre-commit gawk terraform-docs tflint ##### Ubuntu ```bash -sudo apt install python-pip3 gawk &&\ +sudo apt install python3-pip gawk &&\ pip3 install pre-commit curl -L "$(curl -s https://api.github.com/repos/segmentio/terraform-docs/releases/latest | grep -o -E "https://.+?-linux-amd64")" > terraform-docs && chmod +x terraform-docs && sudo mv terraform-docs /usr/bin/ curl -L "$(curl -s https://api.github.com/repos/terraform-linters/tflint/releases/latest | grep -o -E "https://.+?_linux_amd64.zip")" > tflint.zip && unzip tflint.zip && rm tflint.zip && sudo mv tflint /usr/bin/ From b0d4a68b892f9543cada942767ed95cb88adeef9 Mon Sep 17 00:00:00 2001 From: Sergei Ivanov Date: Sat, 4 Apr 2020 06:55:01 +0100 Subject: [PATCH 067/214] Allow passing multiple args to terraform-docs (#98) --- terraform_docs.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/terraform_docs.sh b/terraform_docs.sh index a03f12a16..ebb8c4e46 100755 --- a/terraform_docs.sh +++ b/terraform_docs.sh @@ -95,7 +95,8 @@ terraform_docs() { fi if [[ "$terraform_docs_awk_file" == "0" ]]; then - terraform-docs md "$args" ./ > "$tmp_file" + # shellcheck disable=SC2086 + terraform-docs md $args ./ > "$tmp_file" else # Can't append extension for mktemp, so renaming instead tmp_file_docs=$(mktemp "${TMPDIR:-/tmp}/terraform-docs-XXXXXXXXXX") @@ -103,7 +104,8 @@ terraform_docs() { tmp_file_docs_tf="$tmp_file_docs.tf" awk -f "$terraform_docs_awk_file" ./*.tf > "$tmp_file_docs_tf" - terraform-docs md "$args" "$tmp_file_docs_tf" > "$tmp_file" + # shellcheck disable=SC2086 + terraform-docs md $args "$tmp_file_docs_tf" > "$tmp_file" rm -f "$tmp_file_docs_tf" fi From 8cbcd8ea2b3aff6e2c64daefd681b9c07bbd36b8 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Sat, 4 Apr 2020 07:55:09 +0200 Subject: [PATCH 068/214] Updated CHANGELOG --- CHANGELOG.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cc48a41a..bed43a2a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,16 @@ + +## [v1.28.0] - 2020-03-18 + +- Update installation instructions ([#79](https://github.com/antonbabenko/pre-commit-terraform/issues/79)) + + ## [v1.27.0] - 2020-03-02 +- Updated CHANGELOG - corrected tflint documentation ([#95](https://github.com/antonbabenko/pre-commit-terraform/issues/95)) @@ -260,7 +267,8 @@ - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.27.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.28.0...HEAD +[v1.28.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.27.0...v1.28.0 [v1.27.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.26.0...v1.27.0 [v1.26.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.25.0...v1.26.0 [v1.25.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.24.0...v1.25.0 From 7694fb9b9a2187670f55233d0317c05f6b0080d5 Mon Sep 17 00:00:00 2001 From: Nick M <50747025+mcdonnnj@users.noreply.github.com> Date: Sat, 4 Apr 2020 02:17:25 -0400 Subject: [PATCH 069/214] fix: Change terraform_validate hook functionality for subdirectories with terraform files (#100) * Update terraform_validate.sh: -Change to the directory before running terraform validate to use the Terraform configuration for the appropriate working directory. * Neglected to change the terraform validate call to use the default of the current directory. * Several changes to improve functionality: - Switch to checking the path for '*.tf' instead of always checking the current directory. - Try to find a '.terraform' directory (which indicates a `terraform init`) and change to that directory before running `terraform validate`. * Fix the description for the terraform_validate hook to reflect changes that were made in: https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9a4e7200d936e514cba71 * - Clean up comments. - Adjust variable names to better reflect what they are holding. --- .pre-commit-hooks.yaml | 2 +- terraform_validate.sh | 24 ++++++++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index c7f403941..d0b27aa71 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -35,7 +35,7 @@ exclude: \.terraform\/.*$ - id: terraform_validate - name: Terraform validate without variables + name: Terraform validate description: Validates all Terraform configuration files. entry: terraform_validate.sh language: script diff --git a/terraform_validate.sh b/terraform_validate.sh index 50bfd0ba7..3b44ed77f 100755 --- a/terraform_validate.sh +++ b/terraform_validate.sh @@ -15,13 +15,33 @@ done for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do path_uniq="${path_uniq//__REPLACED__SPACE__/ }" - if [[ -n "$(find . -maxdepth 1 -name '*.tf' -print -quit)" ]]; then - if ! terraform validate $path_uniq; then + if [[ -n "$(find $path_uniq -maxdepth 1 -name '*.tf' -print -quit)" ]]; then + + starting_path=$(realpath "$path_uniq") + terraform_path="$path_uniq" + + # Find the relevant .terraform directory (indicating a 'terraform init'), + # but fall through to the current directory. + while [[ "$terraform_path" != "." ]]; do + if [[ -d "$terraform_path/.terraform" ]]; then + break + else + terraform_path=$(dirname "$terraform_path") + fi + done + + validate_path="${path_uniq#"$terraform_path"}" + + # Change to the directory that has been initialized, run validation, then + # change back to the starting directory. + cd "$(realpath "$terraform_path")" + if ! terraform validate $validate_path; then error=1 echo echo "Failed path: $path_uniq" echo "================================" fi + cd "$starting_path" fi done From 29fa14037b7e56e7fe91decbe2d5a528fda9ad37 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Sat, 4 Apr 2020 08:25:11 +0200 Subject: [PATCH 070/214] Updated CHANGELOG --- .chglog/CHANGELOG.tpl.md | 60 ++++++++++++++++++++++++++++++++++++++++ CHANGELOG.md | 40 ++++++++++----------------- 2 files changed, 74 insertions(+), 26 deletions(-) diff --git a/.chglog/CHANGELOG.tpl.md b/.chglog/CHANGELOG.tpl.md index a58b0e452..687d70232 100644 --- a/.chglog/CHANGELOG.tpl.md +++ b/.chglog/CHANGELOG.tpl.md @@ -1,3 +1,7 @@ +# Change Log + +All notable changes to this project will be documented in this file. + {{ if .Versions -}} ## [Unreleased] @@ -5,12 +9,40 @@ {{ range .Unreleased.CommitGroups -}} ### {{ .Title }} {{ range .Commits -}} +{{/* SKIPPING RULES - START */ -}} +{{- if not (hasPrefix .Subject "Updated CHANGELOG") -}} +{{- if not (contains .Subject "[ci skip]") -}} +{{- if not (contains .Subject "[skip ci]") -}} +{{- if not (hasPrefix .Subject "Merge pull request ") -}} +{{- if not (hasPrefix .Subject "Added CHANGELOG") -}} +{{- /* SKIPPING RULES - END */ -}} - {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} +{{/* SKIPPING RULES - START */ -}} +{{ end -}} +{{ end -}} +{{ end -}} +{{ end -}} +{{ end -}} +{{/* SKIPPING RULES - END */ -}} {{ end }} {{ end -}} {{ else }} {{ range .Unreleased.Commits -}} +{{/* SKIPPING RULES - START */ -}} +{{- if not (hasPrefix .Subject "Updated CHANGELOG") -}} +{{- if not (contains .Subject "[ci skip]") -}} +{{- if not (contains .Subject "[skip ci]") -}} +{{- if not (hasPrefix .Subject "Merge pull request ") -}} +{{- if not (hasPrefix .Subject "Added CHANGELOG") -}} +{{- /* SKIPPING RULES - END */ -}} - {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} +{{/* SKIPPING RULES - START */ -}} +{{ end -}} +{{ end -}} +{{ end -}} +{{ end -}} +{{ end -}} +{{/* SKIPPING RULES - END */ -}} {{ end }} {{ end -}} {{ end -}} @@ -22,12 +54,40 @@ {{ range .CommitGroups -}} ### {{ .Title }} {{ range .Commits -}} +{{/* SKIPPING RULES - START */ -}} +{{- if not (hasPrefix .Subject "Updated CHANGELOG") -}} +{{- if not (contains .Subject "[ci skip]") -}} +{{- if not (contains .Subject "[skip ci]") -}} +{{- if not (hasPrefix .Subject "Merge pull request ") -}} +{{- if not (hasPrefix .Subject "Added CHANGELOG") -}} +{{- /* SKIPPING RULES - END */ -}} - {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} +{{/* SKIPPING RULES - START */ -}} +{{ end -}} +{{ end -}} +{{ end -}} +{{ end -}} +{{ end -}} +{{/* SKIPPING RULES - END */ -}} {{ end }} {{ end -}} {{ else }} {{ range .Commits -}} +{{/* SKIPPING RULES - START */ -}} +{{- if not (hasPrefix .Subject "Updated CHANGELOG") -}} +{{- if not (contains .Subject "[ci skip]") -}} +{{- if not (contains .Subject "[skip ci]") -}} +{{- if not (hasPrefix .Subject "Merge pull request ") -}} +{{- if not (hasPrefix .Subject "Added CHANGELOG") -}} +{{- /* SKIPPING RULES - END */ -}} - {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} +{{/* SKIPPING RULES - START */ -}} +{{ end -}} +{{ end -}} +{{ end -}} +{{ end -}} +{{ end -}} +{{/* SKIPPING RULES - END */ -}} {{ end }} {{ end -}} diff --git a/CHANGELOG.md b/CHANGELOG.md index bed43a2a6..fdb2981d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,25 +1,34 @@ +# Change Log + +All notable changes to this project will be documented in this file. + ## [Unreleased] + +## [v1.29.0] - 2020-04-04 + +- fix: Change terraform_validate hook functionality for subdirectories with terraform files ([#100](https://github.com/antonbabenko/pre-commit-terraform/issues/100)) + + -## [v1.28.0] - 2020-03-18 +## [v1.28.0] - 2020-04-04 +- Allow passing multiple args to terraform-docs ([#98](https://github.com/antonbabenko/pre-commit-terraform/issues/98)) - Update installation instructions ([#79](https://github.com/antonbabenko/pre-commit-terraform/issues/79)) ## [v1.27.0] - 2020-03-02 -- Updated CHANGELOG - corrected tflint documentation ([#95](https://github.com/antonbabenko/pre-commit-terraform/issues/95)) ## [v1.26.0] - 2020-02-21 -- Updated CHANGELOG - Updated pre-commit-hooks - Fixed exit code for terraform 0.11 branch in terraform_docs ([#94](https://github.com/antonbabenko/pre-commit-terraform/issues/94)) @@ -27,42 +36,36 @@ ## [v1.25.0] - 2020-01-30 -- Updated CHANGELOG - Fixed tflint hook to iterate over files ([#77](https://github.com/antonbabenko/pre-commit-terraform/issues/77)) ## [v1.24.0] - 2020-01-21 -- Updated CHANGELOG - Added shfmt to autoformat shell scripts ([#86](https://github.com/antonbabenko/pre-commit-terraform/issues/86)) ## [v1.23.0] - 2020-01-21 -- Updated CHANGELOG - Added support for terraform-docs 0.8.0 with proper support for Terraform 0.12 syntax (bye-bye awk) ([#85](https://github.com/antonbabenko/pre-commit-terraform/issues/85)) ## [v1.22.0] - 2020-01-13 -- Updated CHANGELOG - move terraform-docs args after markdown command ([#83](https://github.com/antonbabenko/pre-commit-terraform/issues/83)) ## [v1.21.0] - 2019-11-16 -- Updated CHANGELOG - use getopt for args in the tflint hook, following the approach in terraform-docs ([#75](https://github.com/antonbabenko/pre-commit-terraform/issues/75)) ## [v1.20.0] - 2019-11-02 -- Updated CHANGELOG - Fixes [#65](https://github.com/antonbabenko/pre-commit-terraform/issues/65): terraform-docs should not fail if complex types contain 'description' keyword ([#73](https://github.com/antonbabenko/pre-commit-terraform/issues/73)) - Added FUNDING.yml - Improve installation instructions and make README more readable ([#72](https://github.com/antonbabenko/pre-commit-terraform/issues/72)) @@ -72,7 +75,6 @@ ## [v1.19.0] - 2019-08-20 -- Updated CHANGELOG - Updated README with terraform_tflint hook - Added support for TFLint with --deep parameter ([#53](https://github.com/antonbabenko/pre-commit-terraform/issues/53)) @@ -80,7 +82,6 @@ ## [v1.18.0] - 2019-08-20 -- Updated CHANGELOG - Updated README with terragrunt_fmt hook - Formatter for Terragrunt HCL files ([#60](https://github.com/antonbabenko/pre-commit-terraform/issues/60)) @@ -88,7 +89,6 @@ ## [v1.17.0] - 2019-06-25 -- Updated CHANGELOG - Fixed enquoted types in terraform_docs (fixed [#52](https://github.com/antonbabenko/pre-commit-terraform/issues/52)) - Fix typo in README ([#51](https://github.com/antonbabenko/pre-commit-terraform/issues/51)) @@ -96,14 +96,12 @@ ## [v1.16.0] - 2019-06-18 -- Updated CHANGELOG - Add slash to mktemp dir (fixed [#50](https://github.com/antonbabenko/pre-commit-terraform/issues/50)) ## [v1.15.0] - 2019-06-18 -- Updated CHANGELOG - Fixed awk script for terraform-docs (kudos [@cytopia](https://github.com/cytopia)) and mktemp on Mac (closes [#47](https://github.com/antonbabenko/pre-commit-terraform/issues/47), [#48](https://github.com/antonbabenko/pre-commit-terraform/issues/48), [#49](https://github.com/antonbabenko/pre-commit-terraform/issues/49)) - Fix version in README.md ([#46](https://github.com/antonbabenko/pre-commit-terraform/issues/46)) @@ -111,21 +109,18 @@ ## [v1.14.0] - 2019-06-17 -- Updated CHANGELOG - Upgraded to work with Terraform >= 0.12 ([#44](https://github.com/antonbabenko/pre-commit-terraform/issues/44)) ## [v1.13.0] - 2019-06-17 -- Updated CHANGELOG - Added support for terraform_docs for Terraform 0.12 ([#45](https://github.com/antonbabenko/pre-commit-terraform/issues/45)) ## [v1.12.0] - 2019-05-27 -- Updated CHANGELOG - Added note about incompatibility of terraform-docs with Terraform 0.12 ([#41](https://github.com/antonbabenko/pre-commit-terraform/issues/41)) - Fixed broken "maintained badge" - Update README.md ([#36](https://github.com/antonbabenko/pre-commit-terraform/issues/36)) @@ -148,23 +143,19 @@ ## [v1.9.0] - 2019-02-18 -- Added CHANGELOG.md - Added chglog (hi [@robinbowes](https://github.com/robinbowes) :)) -- Merge pull request [#33](https://github.com/antonbabenko/pre-commit-terraform/issues/33) from chrisgilmerproj/run_terraform_docs_in_serial - Require terraform-docs runs in serial to avoid pre-commit doing parallel operations on similar file paths ## [v1.8.1] - 2018-12-15 -- Merge pull request [#30](https://github.com/antonbabenko/pre-commit-terraform/issues/30) from RothAndrew/feature/fix_issue_29 - Fix bug not letting terraform_docs_replace work in the root directory of a repo ## [v1.8.0] - 2018-12-14 -- Merge pull request [#27](https://github.com/antonbabenko/pre-commit-terraform/issues/27) from RothAndrew/feature/new_hook - fix typo - Address requested changes - Add `--dest` argument @@ -177,7 +168,6 @@ - Merge remote-tracking branch 'origin/master' into pr25 - Added followup after [#25](https://github.com/antonbabenko/pre-commit-terraform/issues/25) -- Merge pull request [#25](https://github.com/antonbabenko/pre-commit-terraform/issues/25) from getcloudnative/feat-pass-terraform-docs-opts - Add feature to pass options to terraform-docs. - Added license file (fixed [#21](https://github.com/antonbabenko/pre-commit-terraform/issues/21)) @@ -238,8 +228,6 @@ - Added badges - Added formatting for tfvars (fixes [#4](https://github.com/antonbabenko/pre-commit-terraform/issues/4)) ([#6](https://github.com/antonbabenko/pre-commit-terraform/issues/6)) -- Merge pull request [#5](https://github.com/antonbabenko/pre-commit-terraform/issues/5) from schneems/schneems/codetriage-badge -- [ci skip] Get more Open Source Helpers @@ -248,7 +236,6 @@ - Renamed shell script file to the correct one - Updated .pre-commit-hooks.yaml - Updated sha in README -- Merge pull request [#3](https://github.com/antonbabenko/pre-commit-terraform/issues/3) from pecigonzalo/master - Exclude .terraform even on subfolders @@ -267,7 +254,8 @@ - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.28.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.29.0...HEAD +[v1.29.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.28.0...v1.29.0 [v1.28.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.27.0...v1.28.0 [v1.27.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.26.0...v1.27.0 [v1.26.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.25.0...v1.26.0 From 2be8fe54537c12e98188963266bb5fabc79b527b Mon Sep 17 00:00:00 2001 From: Jon Proietti <45764555+jon-proietti-nutrien@users.noreply.github.com> Date: Thu, 23 Apr 2020 09:56:33 -0500 Subject: [PATCH 071/214] feat: Support for TFSec (#103) --- .pre-commit-hooks.yaml | 6 + README.md | 20 +- terraform_tfsec.sh | 551 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 576 insertions(+), 1 deletion(-) create mode 100755 terraform_tfsec.sh diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index d0b27aa71..b2deed7b0 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -57,3 +57,9 @@ language: script files: (\.hcl)$ exclude: \.terraform\/.*$ + +- id: terraform_tfsec + name: Terraform validate with tfsec + description: Static analysis of Terraform templates to spot potential security issues. + entry: terraform_tfsec.sh + language: script diff --git a/README.md b/README.md index 3dd71204a..9e1695da7 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,13 @@ * [`pre-commit`](https://pre-commit.com/#install) * [`terraform-docs`](https://github.com/segmentio/terraform-docs) required for `terraform_docs` hooks. `GNU awk` is required if using `terraform-docs` older than 0.8.0 with Terraform 0.12. * [`TFLint`](https://github.com/terraform-linters/tflint) required for `terraform_tflint` hook. +* [`TFSec`](https://github.com/liamg/tfsec) required for `terraform_tfsec` hook. ##### MacOS ```bash -brew install pre-commit gawk terraform-docs tflint +brew tap liamg/tfsec +brew install pre-commit gawk terraform-docs tflint tfsec ``` ##### Ubuntu @@ -23,6 +25,7 @@ sudo apt install python3-pip gawk &&\ pip3 install pre-commit curl -L "$(curl -s https://api.github.com/repos/segmentio/terraform-docs/releases/latest | grep -o -E "https://.+?-linux-amd64")" > terraform-docs && chmod +x terraform-docs && sudo mv terraform-docs /usr/bin/ curl -L "$(curl -s https://api.github.com/repos/terraform-linters/tflint/releases/latest | grep -o -E "https://.+?_linux_amd64.zip")" > tflint.zip && unzip tflint.zip && rm tflint.zip && sudo mv tflint /usr/bin/ +env GO111MODULE=on go get -u github.com/liamg/tfsec/cmd/tfsec ``` ### 2. Install the pre-commit hook globally @@ -69,6 +72,7 @@ There are several [pre-commit](https://pre-commit.com/) hooks to keep Terraform | `terraform_docs_replace` | Runs `terraform-docs` and pipes the output directly to README.md | | `terraform_tflint` | Validates all Terraform configuration files with [TFLint](https://github.com/terraform-linters/tflint). | | `terragrunt_fmt` | Rewrites all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) to a canonical format. | +| `terraform_tfsec` | [TFSec](https://github.com/liamg/tfsec) static analysis of terraform templates to spot potential security issues. | Check the [source file](https://github.com/antonbabenko/pre-commit-terraform/blob/master/.pre-commit-hooks.yaml) to know arguments used for each hook. @@ -112,6 +116,20 @@ if they are present in `README.md`. - 'args=--enable-rule=terraform_documented_variables' ``` +## Notes about terraform_tfsec hooks + +1. `terraform_tfsec` will recurse all directories/modules. +1. To ignore specific warnings, follow the convention from the +[documentation](https://github.com/liamg/tfsec#ignoring-warnings). + 1. Example: + ```hcl + resource "aws_security_group_rule" "my-rule" { + type = "ingress" + cidr_blocks = ["0.0.0.0/0"] #tfsec:ignore:AWS006 + } + ``` + + ## Notes for developers 1. Python hooks are supported now too. All you have to do is: diff --git a/terraform_tfsec.sh b/terraform_tfsec.sh new file mode 100755 index 000000000..78aaf0945 --- /dev/null +++ b/terraform_tfsec.sh @@ -0,0 +1,551 @@ +#!/usr/bin/env bash +set -e + +main() { + declare argv + argv=$(getopt -o a: --long args: -- "$@") || return + eval "set -- $argv" + + declare args + declare files + + for argv; do + case $argv in + -a | --args) + shift + args="$1" + shift + ;; + --) + shift + files="$@" + break + ;; + esac + done + + tfsec_ "$args" "$files" +} + +tfsec_() { + # Ignore $files because tfsec will recurse directories anyway. + tfsec $args . +} + +getopt() { + # pure-getopt, a drop-in replacement for GNU getopt in pure Bash. + # version 1.4.3 + # + # Copyright 2012-2018 Aron Griffis + # + # Permission is hereby granted, free of charge, to any person obtaining + # a copy of this software and associated documentation files (the + # "Software"), to deal in the Software without restriction, including + # without limitation the rights to use, copy, modify, merge, publish, + # distribute, sublicense, and/or sell copies of the Software, and to + # permit persons to whom the Software is furnished to do so, subject to + # the following conditions: + # + # The above copyright notice and this permission notice shall be included + # in all copies or substantial portions of the Software. + # + # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + _getopt_main() { + # Returns one of the following statuses: + # 0 success + # 1 error parsing parameters + # 2 error in getopt invocation + # 3 internal error + # 4 reserved for -T + # + # For statuses 0 and 1, generates normalized and shell-quoted + # "options -- parameters" on stdout. + + declare parsed status + declare short long name flags + declare have_short=false + + # Synopsis from getopt man-page: + # + # getopt optstring parameters + # getopt [options] [--] optstring parameters + # getopt [options] -o|--options optstring [options] [--] parameters + # + # The first form can be normalized to the third form which + # _getopt_parse() understands. The second form can be recognized after + # first parse when $short hasn't been set. + + if [[ -n ${GETOPT_COMPATIBLE+isset} || $1 == [^-]* ]]; then + # Enable compatibility mode + flags=c$flags + # Normalize first to third synopsis form + set -- -o "$1" -- "${@:2}" + fi + + # First parse always uses flags=p since getopt always parses its own + # arguments effectively in this mode. + parsed=$(_getopt_parse getopt ahl:n:o:qQs:TuV \ + alternative,help,longoptions:,name:,options:,quiet,quiet-output,shell:,test,version \ + p "$@") + status=$? + if [[ $status != 0 ]]; then + if [[ $status == 1 ]]; then + echo "Try \`getopt --help' for more information." >&2 + # Since this is the first parse, convert status 1 to 2 + status=2 + fi + return $status + fi + eval "set -- $parsed" + + while [[ $# -gt 0 ]]; do + case $1 in + -a | --alternative) + flags=a$flags + ;; + + -h | --help) + _getopt_help + return 2 # as does GNU getopt + ;; + + -l | --longoptions) + long="$long${long:+,}$2" + shift + ;; + + -n | --name) + name=$2 + shift + ;; + + -o | --options) + short=$2 + have_short=true + shift + ;; + + -q | --quiet) + flags=q$flags + ;; + + -Q | --quiet-output) + flags=Q$flags + ;; + + -s | --shell) + case $2 in + sh | bash) + flags=${flags//t/} + ;; + csh | tcsh) + flags=t$flags + ;; + *) + echo 'getopt: unknown shell after -s or --shell argument' >&2 + echo "Try \`getopt --help' for more information." >&2 + return 2 + ;; + esac + shift + ;; + + -u | --unquoted) + flags=u$flags + ;; + + -T | --test) + return 4 + ;; + + -V | --version) + echo "pure-getopt 1.4.3" + return 0 + ;; + + --) + shift + break + ;; + esac + + shift + done + + if ! $have_short; then + # $short was declared but never set, not even to an empty string. + # This implies the second form in the synopsis. + if [[ $# == 0 ]]; then + echo 'getopt: missing optstring argument' >&2 + echo "Try \`getopt --help' for more information." >&2 + return 2 + fi + short=$1 + have_short=true + shift + fi + + if [[ $short == -* ]]; then + # Leading dash means generate output in place rather than reordering, + # unless we're already in compatibility mode. + [[ $flags == *c* ]] || flags=i$flags + short=${short#?} + elif [[ $short == +* ]]; then + # Leading plus means POSIXLY_CORRECT, unless we're already in + # compatibility mode. + [[ $flags == *c* ]] || flags=p$flags + short=${short#?} + fi + + # This should fire if POSIXLY_CORRECT is in the environment, even if + # it's an empty string. That's the difference between :+ and + + flags=${POSIXLY_CORRECT+p}$flags + + _getopt_parse "${name:-getopt}" "$short" "$long" "$flags" "$@" + } + + _getopt_parse() { + # Inner getopt parser, used for both first parse and second parse. + # Returns 0 for success, 1 for error parsing, 3 for internal error. + # In the case of status 1, still generates stdout with whatever could + # be parsed. + # + # $flags is a string of characters with the following meanings: + # a - alternative parsing mode + # c - GETOPT_COMPATIBLE + # i - generate output in place rather than reordering + # p - POSIXLY_CORRECT + # q - disable error reporting + # Q - disable normal output + # t - quote for csh/tcsh + # u - unquoted output + + declare name="$1" short="$2" long="$3" flags="$4" + shift 4 + + # Split $long on commas, prepend double-dashes, strip colons; + # for use with _getopt_resolve_abbrev + declare -a longarr + _getopt_split longarr "$long" + longarr=("${longarr[@]/#/--}") + longarr=("${longarr[@]%:}") + longarr=("${longarr[@]%:}") + + # Parse and collect options and parameters + declare -a opts params + declare o alt_recycled=false error=0 + + while [[ $# -gt 0 ]]; do + case $1 in + --) + params=("${params[@]}" "${@:2}") + break + ;; + + --*=*) + o=${1%%=*} + if ! o=$(_getopt_resolve_abbrev "$o" "${longarr[@]}"); then + error=1 + elif [[ ,"$long", == *,"${o#--}"::,* ]]; then + opts=("${opts[@]}" "$o" "${1#*=}") + elif [[ ,"$long", == *,"${o#--}":,* ]]; then + opts=("${opts[@]}" "$o" "${1#*=}") + elif [[ ,"$long", == *,"${o#--}",* ]]; then + if $alt_recycled; then o=${o#-}; fi + _getopt_err "$name: option '$o' doesn't allow an argument" + error=1 + else + echo "getopt: assertion failed (1)" >&2 + return 3 + fi + alt_recycled=false + ;; + + --?*) + o=$1 + if ! o=$(_getopt_resolve_abbrev "$o" "${longarr[@]}"); then + error=1 + elif [[ ,"$long", == *,"${o#--}",* ]]; then + opts=("${opts[@]}" "$o") + elif [[ ,"$long", == *,"${o#--}::",* ]]; then + opts=("${opts[@]}" "$o" '') + elif [[ ,"$long", == *,"${o#--}:",* ]]; then + if [[ $# -ge 2 ]]; then + shift + opts=("${opts[@]}" "$o" "$1") + else + if $alt_recycled; then o=${o#-}; fi + _getopt_err "$name: option '$o' requires an argument" + error=1 + fi + else + echo "getopt: assertion failed (2)" >&2 + return 3 + fi + alt_recycled=false + ;; + + -*) + if [[ $flags == *a* ]]; then + # Alternative parsing mode! + # Try to handle as a long option if any of the following apply: + # 1. There's an equals sign in the mix -x=3 or -xy=3 + # 2. There's 2+ letters and an abbreviated long match -xy + # 3. There's a single letter and an exact long match + # 4. There's a single letter and no short match + o=${1::2} # temp for testing #4 + if [[ $1 == *=* || $1 == -?? || \ + ,$long, == *,"${1#-}"[:,]* || \ + ,$short, != *,"${o#-}"[:,]* ]]; then + o=$(_getopt_resolve_abbrev "${1%%=*}" "${longarr[@]}" 2> /dev/null) + case $? in + 0) + # Unambiguous match. Let the long options parser handle + # it, with a flag to get the right error message. + set -- "-$1" "${@:2}" + alt_recycled=true + continue + ;; + 1) + # Ambiguous match, generate error and continue. + _getopt_resolve_abbrev "${1%%=*}" "${longarr[@]}" > /dev/null + error=1 + shift + continue + ;; + 2) + # No match, fall through to single-character check. + true + ;; + *) + echo "getopt: assertion failed (3)" >&2 + return 3 + ;; + esac + fi + fi + + o=${1::2} + if [[ "$short" == *"${o#-}"::* ]]; then + if [[ ${#1} -gt 2 ]]; then + opts=("${opts[@]}" "$o" "${1:2}") + else + opts=("${opts[@]}" "$o" '') + fi + elif [[ "$short" == *"${o#-}":* ]]; then + if [[ ${#1} -gt 2 ]]; then + opts=("${opts[@]}" "$o" "${1:2}") + elif [[ $# -ge 2 ]]; then + shift + opts=("${opts[@]}" "$o" "$1") + else + _getopt_err "$name: option requires an argument -- '${o#-}'" + error=1 + fi + elif [[ "$short" == *"${o#-}"* ]]; then + opts=("${opts[@]}" "$o") + if [[ ${#1} -gt 2 ]]; then + set -- "$o" "-${1:2}" "${@:2}" + fi + else + if [[ $flags == *a* ]]; then + # Alternative parsing mode! Report on the entire failed + # option. GNU includes =value but we omit it for sanity with + # very long values. + _getopt_err "$name: unrecognized option '${1%%=*}'" + else + _getopt_err "$name: invalid option -- '${o#-}'" + if [[ ${#1} -gt 2 ]]; then + set -- "$o" "-${1:2}" "${@:2}" + fi + fi + error=1 + fi + ;; + + *) + # GNU getopt in-place mode (leading dash on short options) + # overrides POSIXLY_CORRECT + if [[ $flags == *i* ]]; then + opts=("${opts[@]}" "$1") + elif [[ $flags == *p* ]]; then + params=("${params[@]}" "$@") + break + else + params=("${params[@]}" "$1") + fi + ;; + esac + + shift + done + + if [[ $flags == *Q* ]]; then + true # generate no output + else + echo -n ' ' + if [[ $flags == *[cu]* ]]; then + printf '%s -- %s' "${opts[*]}" "${params[*]}" + else + if [[ $flags == *t* ]]; then + _getopt_quote_csh "${opts[@]}" -- "${params[@]}" + else + _getopt_quote "${opts[@]}" -- "${params[@]}" + fi + fi + echo + fi + + return $error + } + + _getopt_err() { + if [[ $flags != *q* ]]; then + printf '%s\n' "$1" >&2 + fi + } + + _getopt_resolve_abbrev() { + # Resolves an abbrevation from a list of possibilities. + # If the abbreviation is unambiguous, echoes the expansion on stdout + # and returns 0. If the abbreviation is ambiguous, prints a message on + # stderr and returns 1. (For first parse this should convert to exit + # status 2.) If there is no match at all, prints a message on stderr + # and returns 2. + declare a q="$1" + declare -a matches + shift + for a; do + if [[ $q == "$a" ]]; then + # Exact match. Squash any other partial matches. + matches=("$a") + break + elif [[ $flags == *a* && $q == -[^-]* && $a == -"$q" ]]; then + # Exact alternative match. Squash any other partial matches. + matches=("$a") + break + elif [[ $a == "$q"* ]]; then + # Abbreviated match. + matches=("${matches[@]}" "$a") + elif [[ $flags == *a* && $q == -[^-]* && $a == -"$q"* ]]; then + # Abbreviated alternative match. + matches=("${matches[@]}" "${a#-}") + fi + done + case ${#matches[@]} in + 0) + [[ $flags == *q* ]] || + printf "$name: unrecognized option %s\\n" \ + "$(_getopt_quote "$q")" >&2 + + return 2 + ;; + 1) + printf '%s' "${matches[0]}" + return 0 + ;; + *) + [[ $flags == *q* ]] || + printf "$name: option %s is ambiguous; possibilities: %s\\n" \ + "$(_getopt_quote "$q")" "$(_getopt_quote "${matches[@]}")" >&2 + + return 1 + ;; + esac + } + + _getopt_split() { + # Splits $2 at commas to build array specified by $1 + declare IFS=, + eval "$1=( \$2 )" + } + + _getopt_quote() { + # Quotes arguments with single quotes, escaping inner single quotes + declare s space q=\' + for s; do + printf "$space'%s'" "${s//$q/$q\\$q$q}" + space=' ' + done + } + + _getopt_quote_csh() { + # Quotes arguments with single quotes, escaping inner single quotes, + # bangs, backslashes and newlines + declare s i c space + for s; do + echo -n "$space'" + for ((i = 0; i < ${#s}; i++)); do + c=${s:i:1} + case $c in + \\ | \' | !) + echo -n "'\\$c'" + ;; + $'\n') + echo -n "\\$c" + ;; + *) + echo -n "$c" + ;; + esac + done + echo -n \' + space=' ' + done + } + + _getopt_help() { + cat <<- EOT >&2 + Usage: + getopt + getopt [options] [--] + getopt [options] -o|--options [options] [--] + Parse command options. + Options: + -a, --alternative allow long options starting with single - + -l, --longoptions the long options to be recognized + -n, --name the name under which errors are reported + -o, --options the short options to be recognized + -q, --quiet disable error reporting by getopt(3) + -Q, --quiet-output no normal output + -s, --shell set quoting conventions to those of + -T, --test test for getopt(1) version + -u, --unquoted do not quote the output + -h, --help display this help and exit + -V, --version output version information and exit + For more details see getopt(1). + EOT + } + + _getopt_version_check() { + if [[ -z $BASH_VERSION ]]; then + echo "getopt: unknown version of bash might not be compatible" >&2 + return 1 + fi + + # This is a lexical comparison that should be sufficient forever. + if [[ $BASH_VERSION < 2.05b ]]; then + echo "getopt: bash $BASH_VERSION might not be compatible" >&2 + return 1 + fi + + return 0 + } + + _getopt_version_check + _getopt_main "$@" + declare status=$? + unset -f _getopt_main _getopt_err _getopt_parse _getopt_quote \ + _getopt_quote_csh _getopt_resolve_abbrev _getopt_split _getopt_help \ + _getopt_version_check + return $status +} + +[[ $BASH_SOURCE != "$0" ]] || main "$@" From 5bd4e2e14b0fd4584150e82cd5313e8f4e387c4f Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Thu, 23 Apr 2020 16:58:40 +0200 Subject: [PATCH 072/214] Updated pre-commit deps --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 460616c1e..9b4a49475 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: - id: check-merge-conflict - id: check-executables-have-shebangs - repo: git://github.com/jumanjihouse/pre-commit-hooks - rev: 1.11.2 + rev: 2.0.0 hooks: - id: shfmt args: ['-l', '-i', '2', '-ci', '-sr', '-w'] From 750e16dec0b3fae63469c9839b9d187472039a3d Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Thu, 23 Apr 2020 17:00:31 +0200 Subject: [PATCH 073/214] Updated CHANGELOG --- CHANGELOG.md | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fdb2981d8..6cd2a9c98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,11 +7,40 @@ All notable changes to this project will be documented in this file. + +## [v1.30.0] - 2020-04-23 + +- Updated pre-commit deps +- feat: Support for TFSec ([#103](https://github.com/antonbabenko/pre-commit-terraform/issues/103)) + + ## [v1.29.0] - 2020-04-04 - fix: Change terraform_validate hook functionality for subdirectories with terraform files ([#100](https://github.com/antonbabenko/pre-commit-terraform/issues/100)) +### + +configuration for the appropriate working directory. + +* Neglected to change the terraform validate call to use the default of the +current directory. + +* Several changes to improve functionality: +- Switch to checking the path for '*.tf' instead of always checking the current + +directory. +- Try to find a '.terraform' directory (which indicates a `terraform init`) and + +change to that directory before running `terraform validate`. + +* Fix the description for the terraform_validate hook to reflect changes that were +made in: +https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9a4e7200d936e514cba71 + +* - Clean up comments. +- Adjust variable names to better reflect what they are holding. + ## [v1.28.0] - 2020-04-04 @@ -254,7 +283,8 @@ All notable changes to this project will be documented in this file. - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.29.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.30.0...HEAD +[v1.30.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.29.0...v1.30.0 [v1.29.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.28.0...v1.29.0 [v1.28.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.27.0...v1.28.0 [v1.27.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.26.0...v1.27.0 From d174ca951460cf5328720b193879d0b6cdf705db Mon Sep 17 00:00:00 2001 From: gchappell99 <44392051+gchappell99@users.noreply.github.com> Date: Wed, 29 Apr 2020 10:35:31 +0100 Subject: [PATCH 074/214] docs: Added coreutils as requirements in README.md (#105) --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9e1695da7..24a49de56 100644 --- a/README.md +++ b/README.md @@ -10,12 +10,13 @@ * [`terraform-docs`](https://github.com/segmentio/terraform-docs) required for `terraform_docs` hooks. `GNU awk` is required if using `terraform-docs` older than 0.8.0 with Terraform 0.12. * [`TFLint`](https://github.com/terraform-linters/tflint) required for `terraform_tflint` hook. * [`TFSec`](https://github.com/liamg/tfsec) required for `terraform_tfsec` hook. +* [`coreutils`](https://formulae.brew.sh/formula/coreutils) required for `terraform_validate` hook on macOS (due to use of `realpath`). ##### MacOS ```bash brew tap liamg/tfsec -brew install pre-commit gawk terraform-docs tflint tfsec +brew install pre-commit gawk terraform-docs tflint tfsec coreutils ``` ##### Ubuntu From 85ec8a49bec3ea8e3b749f2650b92b6da64fdeb0 Mon Sep 17 00:00:00 2001 From: snolan-uturn <50503078+snolan-uturn@users.noreply.github.com> Date: Wed, 29 Apr 2020 15:06:02 -0500 Subject: [PATCH 075/214] docs: Fixed the docs to use the latest config syntax(#106) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 24a49de56..5fdaf034c 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ Step into the repository you want to have the pre-commit hooks installed and run ```bash git init cat < .pre-commit-config.yaml +repos: - repo: git://github.com/antonbabenko/pre-commit-terraform rev: # Get the latest from: https://github.com/antonbabenko/pre-commit-terraform/releases hooks: From 27e63693395ce9965d52e6e23844cc792a145983 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Wed, 27 May 2020 10:35:15 +0200 Subject: [PATCH 076/214] fix: Updated formatting in README (closes #113) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5fdaf034c..9a2b7099a 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,7 @@ if they are present in `README.md`. In order to pass multiple args, try the following: ```yaml - id: terraform_tflint - args: + args: - 'args=--deep' - 'args=--enable-rule=terraform_documented_variables' ``` From 2e40ade8d0bdff4eea5fb64e55d6f40a71768426 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Wed, 27 May 2020 10:36:23 +0200 Subject: [PATCH 077/214] Updated CHANGELOG --- CHANGELOG.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cd2a9c98..75045a71d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ All notable changes to this project will be documented in this file. + +## [v1.31.0] - 2020-05-27 + +- fix: Updated formatting in README (closes [#113](https://github.com/antonbabenko/pre-commit-terraform/issues/113)) +- docs: Fixed the docs to use the latest config syntax([#106](https://github.com/antonbabenko/pre-commit-terraform/issues/106)) +- docs: Added coreutils as requirements in README.md ([#105](https://github.com/antonbabenko/pre-commit-terraform/issues/105)) + + ## [v1.30.0] - 2020-04-23 @@ -283,7 +291,8 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.30.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.31.0...HEAD +[v1.31.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.30.0...v1.31.0 [v1.30.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.29.0...v1.30.0 [v1.29.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.28.0...v1.29.0 [v1.28.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.27.0...v1.28.0 From f6caf2195ac53fd31990a526e20f7a05ca37466d Mon Sep 17 00:00:00 2001 From: Prahalad Ramji Date: Wed, 19 Aug 2020 20:01:42 +1000 Subject: [PATCH 078/214] feat: add terragrunt validate hook (#134) --- .pre-commit-hooks.yaml | 8 ++++++++ README.md | 1 + terragrunt_validate.sh | 23 +++++++++++++++++++++++ 3 files changed, 32 insertions(+) create mode 100755 terragrunt_validate.sh diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index b2deed7b0..94fe7d432 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -58,6 +58,14 @@ files: (\.hcl)$ exclude: \.terraform\/.*$ +- id: terragrunt_validate + name: Terragrunt validate + description: Validates all Terragrunt configuration files. + entry: terragrunt_validate.sh + language: script + files: (\.hcl)$ + exclude: \.terraform\/.*$ + - id: terraform_tfsec name: Terraform validate with tfsec description: Static analysis of Terraform templates to spot potential security issues. diff --git a/README.md b/README.md index 9a2b7099a..e87f94785 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,7 @@ There are several [pre-commit](https://pre-commit.com/) hooks to keep Terraform | `terraform_docs_replace` | Runs `terraform-docs` and pipes the output directly to README.md | | `terraform_tflint` | Validates all Terraform configuration files with [TFLint](https://github.com/terraform-linters/tflint). | | `terragrunt_fmt` | Rewrites all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) to a canonical format. | +| `terragrunt_validate` | Validates all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) | | `terraform_tfsec` | [TFSec](https://github.com/liamg/tfsec) static analysis of terraform templates to spot potential security issues. | Check the [source file](https://github.com/antonbabenko/pre-commit-terraform/blob/master/.pre-commit-hooks.yaml) to know arguments used for each hook. diff --git a/terragrunt_validate.sh b/terragrunt_validate.sh new file mode 100755 index 000000000..7f0cf5849 --- /dev/null +++ b/terragrunt_validate.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +set -e + +declare -a paths + +index=0 + +for file_with_path in "$@"; do + file_with_path="${file_with_path// /__REPLACED__SPACE__}" + + paths[index]=$(dirname "$file_with_path") + + let "index+=1" +done + +for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do + path_uniq="${path_uniq//__REPLACED__SPACE__/ }" + + pushd "$path_uniq" > /dev/null + terragrunt validate + popd > /dev/null +done From dafe42560ace053a00e5902931972ba9f28b7d5c Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Wed, 19 Aug 2020 12:02:43 +0200 Subject: [PATCH 079/214] Updated CHANGELOG --- CHANGELOG.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75045a71d..a6b31e40a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ All notable changes to this project will be documented in this file. + +## [v1.32.0] - 2020-08-19 + +- feat: add terragrunt validate hook ([#134](https://github.com/antonbabenko/pre-commit-terraform/issues/134)) + + ## [v1.31.0] - 2020-05-27 @@ -291,7 +297,8 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.31.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.32.0...HEAD +[v1.32.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.31.0...v1.32.0 [v1.31.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.30.0...v1.31.0 [v1.30.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.29.0...v1.30.0 [v1.29.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.28.0...v1.29.0 From 994653c507cd67a160b066e1552e6f6b5df25ec0 Mon Sep 17 00:00:00 2001 From: Khosrow Moossavi Date: Wed, 19 Aug 2020 06:06:55 -0400 Subject: [PATCH 080/214] docs: Update terraform-docs link pointing to new organization (#130) --- README.md | 6 +++--- terraform_docs.sh | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e87f94785..78f995158 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ ### 1. Install dependencies * [`pre-commit`](https://pre-commit.com/#install) -* [`terraform-docs`](https://github.com/segmentio/terraform-docs) required for `terraform_docs` hooks. `GNU awk` is required if using `terraform-docs` older than 0.8.0 with Terraform 0.12. +* [`terraform-docs`](https://github.com/terraform-docs/terraform-docs) required for `terraform_docs` hooks. `GNU awk` is required if using `terraform-docs` older than 0.8.0 with Terraform 0.12. * [`TFLint`](https://github.com/terraform-linters/tflint) required for `terraform_tflint` hook. * [`TFSec`](https://github.com/liamg/tfsec) required for `terraform_tfsec` hook. * [`coreutils`](https://formulae.brew.sh/formula/coreutils) required for `terraform_validate` hook on macOS (due to use of `realpath`). @@ -24,7 +24,7 @@ brew install pre-commit gawk terraform-docs tflint tfsec coreutils ```bash sudo apt install python3-pip gawk &&\ pip3 install pre-commit -curl -L "$(curl -s https://api.github.com/repos/segmentio/terraform-docs/releases/latest | grep -o -E "https://.+?-linux-amd64")" > terraform-docs && chmod +x terraform-docs && sudo mv terraform-docs /usr/bin/ +curl -L "$(curl -s https://api.github.com/repos/terraform-docs/terraform-docs/releases/latest | grep -o -E "https://.+?-linux-amd64")" > terraform-docs && chmod +x terraform-docs && sudo mv terraform-docs /usr/bin/ curl -L "$(curl -s https://api.github.com/repos/terraform-linters/tflint/releases/latest | grep -o -E "https://.+?_linux_amd64.zip")" > tflint.zip && unzip tflint.zip && rm tflint.zip && sudo mv tflint /usr/bin/ env GO111MODULE=on go get -u github.com/liamg/tfsec/cmd/tfsec ``` @@ -81,7 +81,7 @@ Check the [source file](https://github.com/antonbabenko/pre-commit-terraform/blo ## Notes about terraform_docs hooks -1. `terraform_docs` and `terraform_docs_without_aggregate_type_defaults` will insert/update documentation generated by [terraform-docs](https://github.com/segmentio/terraform-docs) framed by markers: +1. `terraform_docs` and `terraform_docs_without_aggregate_type_defaults` will insert/update documentation generated by [terraform-docs](https://github.com/terraform-docs/terraform-docs) framed by markers: ```txt diff --git a/terraform_docs.sh b/terraform_docs.sh index ebb8c4e46..8e7393329 100755 --- a/terraform_docs.sh +++ b/terraform_docs.sh @@ -127,8 +127,8 @@ terraform_docs_awk() { 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/segmentio/terraform-docs/ -# https://github.com/segmentio/terraform-docs/issues/62 +# 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 ~ /\{/ ) { From 774c63e772f1d933a066ae59ca74e7137f1d982a Mon Sep 17 00:00:00 2001 From: Robin Bowes Date: Thu, 27 Aug 2020 10:57:45 +0100 Subject: [PATCH 081/214] fix: Pass args and env vars to terraform validate (#125) --- README.md | 41 ++- lib_getopt | 494 +++++++++++++++++++++++++++++++++++++ terraform_tflint.sh | 561 +++--------------------------------------- terraform_validate.sh | 139 ++++++++--- 4 files changed, 669 insertions(+), 566 deletions(-) create mode 100644 lib_getopt diff --git a/README.md b/README.md index 78f995158..4fce40757 100644 --- a/README.md +++ b/README.md @@ -108,15 +108,15 @@ if they are present in `README.md`. ```yaml hooks: - id: terraform_tflint - args: ['args=--deep'] + args: ['--args=--deep'] ``` In order to pass multiple args, try the following: ```yaml - id: terraform_tflint args: - - 'args=--deep' - - 'args=--enable-rule=terraform_documented_variables' + - '--args=--deep' + - '--args=--enable-rule=terraform_documented_variables' ``` ## Notes about terraform_tfsec hooks @@ -132,6 +132,41 @@ if they are present in `README.md`. } ``` +## Notes about terraform_validate hooks + +1. `terraform_validate` supports custom arguments so you can pass supported no-color or json flags. + + 1. Example: + ```yaml + hooks: + - id: terraform_validate + args: ['--args=-json'] + ``` + + In order to pass multiple args, try the following: + ```yaml + - id: terraform_validate + args: + - '--args=-json' + - '--args=-no-color' + ``` +1. `terraform_validate` also supports custom environment variables passed to the pre-commit runtime + + 1. Example: + ```yaml + hooks: + - id: terraform_validate + args: ['--envs=AWS_DEFAULT_REGION="us-west-2"'] + ``` + + In order to pass multiple args, try the following: + ```yaml + - id: terraform_validate + args: + - '--envs=AWS_DEFAULT_REGION="us-west-2"' + - '--envs=AWS_ACCESS_KEY_ID="anaccesskey"' + - '--envs=AWS_SECRET_ACCESS_KEY="asecretkey"' + ``` ## Notes for developers diff --git a/lib_getopt b/lib_getopt new file mode 100644 index 000000000..e360b490b --- /dev/null +++ b/lib_getopt @@ -0,0 +1,494 @@ +#!/bin/bash + +getopt() { + # pure-getopt, a drop-in replacement for GNU getopt in pure Bash. + # version 1.4.3 + # + # Copyright 2012-2018 Aron Griffis + # + # Permission is hereby granted, free of charge, to any person obtaining + # a copy of this software and associated documentation files (the + # "Software"), to deal in the Software without restriction, including + # without limitation the rights to use, copy, modify, merge, publish, + # distribute, sublicense, and/or sell copies of the Software, and to + # permit persons to whom the Software is furnished to do so, subject to + # the following conditions: + # + # The above copyright notice and this permission notice shall be included + # in all copies or substantial portions of the Software. + # + # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + _getopt_main() { + # Returns one of the following statuses: + # 0 success + # 1 error parsing parameters + # 2 error in getopt invocation + # 3 internal error + # 4 reserved for -T + # + # For statuses 0 and 1, generates normalized and shell-quoted + # "options -- parameters" on stdout. + + declare parsed status + declare short long name flags + declare have_short=false + + # Synopsis from getopt man-page: + # + # getopt optstring parameters + # getopt [options] [--] optstring parameters + # getopt [options] -o|--options optstring [options] [--] parameters + # + # The first form can be normalized to the third form which + # _getopt_parse() understands. The second form can be recognized after + # first parse when $short hasn't been set. + + if [[ -n ${GETOPT_COMPATIBLE+isset} || $1 == [^-]* ]]; then + # Enable compatibility mode + flags=c$flags + # Normalize first to third synopsis form + set -- -o "$1" -- "${@:2}" + fi + + # First parse always uses flags=p since getopt always parses its own + # arguments effectively in this mode. + parsed=$(_getopt_parse getopt ahl:n:o:qQs:TuV \ + alternative,help,longoptions:,name:,options:,quiet,quiet-output,shell:,test,version \ + p "$@") + status=$? + if [[ $status != 0 ]]; then + if [[ $status == 1 ]]; then + echo "Try \`getopt --help' for more information." >&2 + # Since this is the first parse, convert status 1 to 2 + status=2 + fi + return $status + fi + eval "set -- $parsed" + + while [[ $# -gt 0 ]]; do + case $1 in + (-a|--alternative) + flags=a$flags ;; + + (-h|--help) + _getopt_help + return 2 # as does GNU getopt + ;; + + (-l|--longoptions) + long="$long${long:+,}$2" + shift ;; + + (-n|--name) + name=$2 + shift ;; + + (-o|--options) + short=$2 + have_short=true + shift ;; + + (-q|--quiet) + flags=q$flags ;; + + (-Q|--quiet-output) + flags=Q$flags ;; + + (-s|--shell) + case $2 in + (sh|bash) + flags=${flags//t/} ;; + (csh|tcsh) + flags=t$flags ;; + (*) + echo 'getopt: unknown shell after -s or --shell argument' >&2 + echo "Try \`getopt --help' for more information." >&2 + return 2 ;; + esac + shift ;; + + (-u|--unquoted) + flags=u$flags ;; + + (-T|--test) + return 4 ;; + + (-V|--version) + echo "pure-getopt 1.4.3" + return 0 ;; + + (--) + shift + break ;; + esac + + shift + done + + if ! $have_short; then + # $short was declared but never set, not even to an empty string. + # This implies the second form in the synopsis. + if [[ $# == 0 ]]; then + echo 'getopt: missing optstring argument' >&2 + echo "Try \`getopt --help' for more information." >&2 + return 2 + fi + short=$1 + have_short=true + shift + fi + + if [[ $short == -* ]]; then + # Leading dash means generate output in place rather than reordering, + # unless we're already in compatibility mode. + [[ $flags == *c* ]] || flags=i$flags + short=${short#?} + elif [[ $short == +* ]]; then + # Leading plus means POSIXLY_CORRECT, unless we're already in + # compatibility mode. + [[ $flags == *c* ]] || flags=p$flags + short=${short#?} + fi + + # This should fire if POSIXLY_CORRECT is in the environment, even if + # it's an empty string. That's the difference between :+ and + + flags=${POSIXLY_CORRECT+p}$flags + + _getopt_parse "${name:-getopt}" "$short" "$long" "$flags" "$@" + } + + _getopt_parse() { + # Inner getopt parser, used for both first parse and second parse. + # Returns 0 for success, 1 for error parsing, 3 for internal error. + # In the case of status 1, still generates stdout with whatever could + # be parsed. + # + # $flags is a string of characters with the following meanings: + # a - alternative parsing mode + # c - GETOPT_COMPATIBLE + # i - generate output in place rather than reordering + # p - POSIXLY_CORRECT + # q - disable error reporting + # Q - disable normal output + # t - quote for csh/tcsh + # u - unquoted output + + declare name="$1" short="$2" long="$3" flags="$4" + shift 4 + + # Split $long on commas, prepend double-dashes, strip colons; + # for use with _getopt_resolve_abbrev + declare -a longarr + _getopt_split longarr "$long" + longarr=( "${longarr[@]/#/--}" ) + longarr=( "${longarr[@]%:}" ) + longarr=( "${longarr[@]%:}" ) + + # Parse and collect options and parameters + declare -a opts params + declare o alt_recycled=false error=0 + + while [[ $# -gt 0 ]]; do + case $1 in + (--) + params=( "${params[@]}" "${@:2}" ) + break ;; + + (--*=*) + o=${1%%=*} + if ! o=$(_getopt_resolve_abbrev "$o" "${longarr[@]}"); then + error=1 + elif [[ ,"$long", == *,"${o#--}"::,* ]]; then + opts=( "${opts[@]}" "$o" "${1#*=}" ) + elif [[ ,"$long", == *,"${o#--}":,* ]]; then + opts=( "${opts[@]}" "$o" "${1#*=}" ) + elif [[ ,"$long", == *,"${o#--}",* ]]; then + if $alt_recycled; then o=${o#-}; fi + _getopt_err "$name: option '$o' doesn't allow an argument" + error=1 + else + echo "getopt: assertion failed (1)" >&2 + return 3 + fi + alt_recycled=false + ;; + + (--?*) + o=$1 + if ! o=$(_getopt_resolve_abbrev "$o" "${longarr[@]}"); then + error=1 + elif [[ ,"$long", == *,"${o#--}",* ]]; then + opts=( "${opts[@]}" "$o" ) + elif [[ ,"$long", == *,"${o#--}::",* ]]; then + opts=( "${opts[@]}" "$o" '' ) + elif [[ ,"$long", == *,"${o#--}:",* ]]; then + if [[ $# -ge 2 ]]; then + shift + opts=( "${opts[@]}" "$o" "$1" ) + else + if $alt_recycled; then o=${o#-}; fi + _getopt_err "$name: option '$o' requires an argument" + error=1 + fi + else + echo "getopt: assertion failed (2)" >&2 + return 3 + fi + alt_recycled=false + ;; + + (-*) + if [[ $flags == *a* ]]; then + # Alternative parsing mode! + # Try to handle as a long option if any of the following apply: + # 1. There's an equals sign in the mix -x=3 or -xy=3 + # 2. There's 2+ letters and an abbreviated long match -xy + # 3. There's a single letter and an exact long match + # 4. There's a single letter and no short match + o=${1::2} # temp for testing #4 + if [[ $1 == *=* || $1 == -?? || \ + ,$long, == *,"${1#-}"[:,]* || \ + ,$short, != *,"${o#-}"[:,]* ]]; then + o=$(_getopt_resolve_abbrev "${1%%=*}" "${longarr[@]}" 2>/dev/null) + case $? in + (0) + # Unambiguous match. Let the long options parser handle + # it, with a flag to get the right error message. + set -- "-$1" "${@:2}" + alt_recycled=true + continue ;; + (1) + # Ambiguous match, generate error and continue. + _getopt_resolve_abbrev "${1%%=*}" "${longarr[@]}" >/dev/null + error=1 + shift + continue ;; + (2) + # No match, fall through to single-character check. + true ;; + (*) + echo "getopt: assertion failed (3)" >&2 + return 3 ;; + esac + fi + fi + + o=${1::2} + if [[ "$short" == *"${o#-}"::* ]]; then + if [[ ${#1} -gt 2 ]]; then + opts=( "${opts[@]}" "$o" "${1:2}" ) + else + opts=( "${opts[@]}" "$o" '' ) + fi + elif [[ "$short" == *"${o#-}":* ]]; then + if [[ ${#1} -gt 2 ]]; then + opts=( "${opts[@]}" "$o" "${1:2}" ) + elif [[ $# -ge 2 ]]; then + shift + opts=( "${opts[@]}" "$o" "$1" ) + else + _getopt_err "$name: option requires an argument -- '${o#-}'" + error=1 + fi + elif [[ "$short" == *"${o#-}"* ]]; then + opts=( "${opts[@]}" "$o" ) + if [[ ${#1} -gt 2 ]]; then + set -- "$o" "-${1:2}" "${@:2}" + fi + else + if [[ $flags == *a* ]]; then + # Alternative parsing mode! Report on the entire failed + # option. GNU includes =value but we omit it for sanity with + # very long values. + _getopt_err "$name: unrecognized option '${1%%=*}'" + else + _getopt_err "$name: invalid option -- '${o#-}'" + if [[ ${#1} -gt 2 ]]; then + set -- "$o" "-${1:2}" "${@:2}" + fi + fi + error=1 + fi ;; + + (*) + # GNU getopt in-place mode (leading dash on short options) + # overrides POSIXLY_CORRECT + if [[ $flags == *i* ]]; then + opts=( "${opts[@]}" "$1" ) + elif [[ $flags == *p* ]]; then + params=( "${params[@]}" "$@" ) + break + else + params=( "${params[@]}" "$1" ) + fi + esac + + shift + done + + if [[ $flags == *Q* ]]; then + true # generate no output + else + echo -n ' ' + if [[ $flags == *[cu]* ]]; then + printf '%s -- %s' "${opts[*]}" "${params[*]}" + else + if [[ $flags == *t* ]]; then + _getopt_quote_csh "${opts[@]}" -- "${params[@]}" + else + _getopt_quote "${opts[@]}" -- "${params[@]}" + fi + fi + echo + fi + + return $error + } + + _getopt_err() { + if [[ $flags != *q* ]]; then + printf '%s\n' "$1" >&2 + fi + } + + _getopt_resolve_abbrev() { + # Resolves an abbrevation from a list of possibilities. + # If the abbreviation is unambiguous, echoes the expansion on stdout + # and returns 0. If the abbreviation is ambiguous, prints a message on + # stderr and returns 1. (For first parse this should convert to exit + # status 2.) If there is no match at all, prints a message on stderr + # and returns 2. + declare a q="$1" + declare -a matches + shift + for a; do + if [[ $q == "$a" ]]; then + # Exact match. Squash any other partial matches. + matches=( "$a" ) + break + elif [[ $flags == *a* && $q == -[^-]* && $a == -"$q" ]]; then + # Exact alternative match. Squash any other partial matches. + matches=( "$a" ) + break + elif [[ $a == "$q"* ]]; then + # Abbreviated match. + matches=( "${matches[@]}" "$a" ) + elif [[ $flags == *a* && $q == -[^-]* && $a == -"$q"* ]]; then + # Abbreviated alternative match. + matches=( "${matches[@]}" "${a#-}" ) + fi + done + case ${#matches[@]} in + (0) + [[ $flags == *q* ]] || \ + printf "$name: unrecognized option %s\\n" >&2 \ + "$(_getopt_quote "$q")" + return 2 ;; + (1) + printf '%s' "${matches[0]}"; return 0 ;; + (*) + [[ $flags == *q* ]] || \ + printf "$name: option %s is ambiguous; possibilities: %s\\n" >&2 \ + "$(_getopt_quote "$q")" "$(_getopt_quote "${matches[@]}")" + return 1 ;; + esac + } + + _getopt_split() { + # Splits $2 at commas to build array specified by $1 + declare IFS=, + eval "$1=( \$2 )" + } + + _getopt_quote() { + # Quotes arguments with single quotes, escaping inner single quotes + declare s space q=\' + for s; do + printf "$space'%s'" "${s//$q/$q\\$q$q}" + space=' ' + done + } + + _getopt_quote_csh() { + # Quotes arguments with single quotes, escaping inner single quotes, + # bangs, backslashes and newlines + declare s i c space + for s; do + echo -n "$space'" + for ((i=0; i<${#s}; i++)); do + c=${s:i:1} + case $c in + (\\|\'|!) + echo -n "'\\$c'" ;; + ($'\n') + echo -n "\\$c" ;; + (*) + echo -n "$c" ;; + esac + done + echo -n \' + space=' ' + done + } + + _getopt_help() { + cat <<-EOT >&2 + + Usage: + getopt + getopt [options] [--] + getopt [options] -o|--options [options] [--] + + Parse command options. + + Options: + -a, --alternative allow long options starting with single - + -l, --longoptions the long options to be recognized + -n, --name the name under which errors are reported + -o, --options the short options to be recognized + -q, --quiet disable error reporting by getopt(3) + -Q, --quiet-output no normal output + -s, --shell set quoting conventions to those of + -T, --test test for getopt(1) version + -u, --unquoted do not quote the output + + -h, --help display this help and exit + -V, --version output version information and exit + + For more details see getopt(1). + EOT + } + + _getopt_version_check() { + if [[ -z $BASH_VERSION ]]; then + echo "getopt: unknown version of bash might not be compatible" >&2 + return 1 + fi + + # This is a lexical comparison that should be sufficient forever. + if [[ $BASH_VERSION < 2.05b ]]; then + echo "getopt: bash $BASH_VERSION might not be compatible" >&2 + return 1 + fi + + return 0 + } + + _getopt_version_check + _getopt_main "$@" + declare status=$? + unset -f _getopt_main _getopt_err _getopt_parse _getopt_quote \ + _getopt_quote_csh _getopt_resolve_abbrev _getopt_split _getopt_help \ + _getopt_version_check + return $status +} + +# vim:sw=2 diff --git a/terraform_tflint.sh b/terraform_tflint.sh index b8fcd8f0e..e7a212b89 100755 --- a/terraform_tflint.sh +++ b/terraform_tflint.sh @@ -1,564 +1,73 @@ #!/usr/bin/env bash + set -e main() { + initialize_ + parse_cmdline_ "$@" + tflint_ +} + +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_() { declare argv argv=$(getopt -o a: --long args: -- "$@") || return eval "set -- $argv" - declare args - declare files - for argv; do case $argv in -a | --args) shift - args="$1" + ARGS+=("$1") shift ;; --) shift - files="$@" + FILES=("$@") break ;; esac done - - tflint_ "$args" "$files" } tflint_() { - for file_with_path in $files; do + local index=0 + for file_with_path in "${FILES[@]}"; do file_with_path="${file_with_path// /__REPLACED__SPACE__}" paths[index]=$(dirname "$file_with_path") - let "index+=1" + ((index += 1)) done for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do path_uniq="${path_uniq//__REPLACED__SPACE__/ }" pushd "$path_uniq" > /dev/null - tflint $args + tflint "${ARGS[@]}" popd > /dev/null done } -getopt() { - # pure-getopt, a drop-in replacement for GNU getopt in pure Bash. - # version 1.4.3 - # - # Copyright 2012-2018 Aron Griffis - # - # Permission is hereby granted, free of charge, to any person obtaining - # a copy of this software and associated documentation files (the - # "Software"), to deal in the Software without restriction, including - # without limitation the rights to use, copy, modify, merge, publish, - # distribute, sublicense, and/or sell copies of the Software, and to - # permit persons to whom the Software is furnished to do so, subject to - # the following conditions: - # - # The above copyright notice and this permission notice shall be included - # in all copies or substantial portions of the Software. - # - # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - _getopt_main() { - # Returns one of the following statuses: - # 0 success - # 1 error parsing parameters - # 2 error in getopt invocation - # 3 internal error - # 4 reserved for -T - # - # For statuses 0 and 1, generates normalized and shell-quoted - # "options -- parameters" on stdout. - - declare parsed status - declare short long name flags - declare have_short=false - - # Synopsis from getopt man-page: - # - # getopt optstring parameters - # getopt [options] [--] optstring parameters - # getopt [options] -o|--options optstring [options] [--] parameters - # - # The first form can be normalized to the third form which - # _getopt_parse() understands. The second form can be recognized after - # first parse when $short hasn't been set. - - if [[ -n ${GETOPT_COMPATIBLE+isset} || $1 == [^-]* ]]; then - # Enable compatibility mode - flags=c$flags - # Normalize first to third synopsis form - set -- -o "$1" -- "${@:2}" - fi - - # First parse always uses flags=p since getopt always parses its own - # arguments effectively in this mode. - parsed=$(_getopt_parse getopt ahl:n:o:qQs:TuV \ - alternative,help,longoptions:,name:,options:,quiet,quiet-output,shell:,test,version \ - p "$@") - status=$? - if [[ $status != 0 ]]; then - if [[ $status == 1 ]]; then - echo "Try \`getopt --help' for more information." >&2 - # Since this is the first parse, convert status 1 to 2 - status=2 - fi - return $status - fi - eval "set -- $parsed" - - while [[ $# -gt 0 ]]; do - case $1 in - -a | --alternative) - flags=a$flags - ;; - - -h | --help) - _getopt_help - return 2 # as does GNU getopt - ;; - - -l | --longoptions) - long="$long${long:+,}$2" - shift - ;; - - -n | --name) - name=$2 - shift - ;; - - -o | --options) - short=$2 - have_short=true - shift - ;; - - -q | --quiet) - flags=q$flags - ;; - - -Q | --quiet-output) - flags=Q$flags - ;; - - -s | --shell) - case $2 in - sh | bash) - flags=${flags//t/} - ;; - csh | tcsh) - flags=t$flags - ;; - *) - echo 'getopt: unknown shell after -s or --shell argument' >&2 - echo "Try \`getopt --help' for more information." >&2 - return 2 - ;; - esac - shift - ;; - - -u | --unquoted) - flags=u$flags - ;; - - -T | --test) - return 4 - ;; - - -V | --version) - echo "pure-getopt 1.4.3" - return 0 - ;; - - --) - shift - break - ;; - esac - - shift - done - - if ! $have_short; then - # $short was declared but never set, not even to an empty string. - # This implies the second form in the synopsis. - if [[ $# == 0 ]]; then - echo 'getopt: missing optstring argument' >&2 - echo "Try \`getopt --help' for more information." >&2 - return 2 - fi - short=$1 - have_short=true - shift - fi - - if [[ $short == -* ]]; then - # Leading dash means generate output in place rather than reordering, - # unless we're already in compatibility mode. - [[ $flags == *c* ]] || flags=i$flags - short=${short#?} - elif [[ $short == +* ]]; then - # Leading plus means POSIXLY_CORRECT, unless we're already in - # compatibility mode. - [[ $flags == *c* ]] || flags=p$flags - short=${short#?} - fi - - # This should fire if POSIXLY_CORRECT is in the environment, even if - # it's an empty string. That's the difference between :+ and + - flags=${POSIXLY_CORRECT+p}$flags - - _getopt_parse "${name:-getopt}" "$short" "$long" "$flags" "$@" - } - - _getopt_parse() { - # Inner getopt parser, used for both first parse and second parse. - # Returns 0 for success, 1 for error parsing, 3 for internal error. - # In the case of status 1, still generates stdout with whatever could - # be parsed. - # - # $flags is a string of characters with the following meanings: - # a - alternative parsing mode - # c - GETOPT_COMPATIBLE - # i - generate output in place rather than reordering - # p - POSIXLY_CORRECT - # q - disable error reporting - # Q - disable normal output - # t - quote for csh/tcsh - # u - unquoted output - - declare name="$1" short="$2" long="$3" flags="$4" - shift 4 - - # Split $long on commas, prepend double-dashes, strip colons; - # for use with _getopt_resolve_abbrev - declare -a longarr - _getopt_split longarr "$long" - longarr=("${longarr[@]/#/--}") - longarr=("${longarr[@]%:}") - longarr=("${longarr[@]%:}") - - # Parse and collect options and parameters - declare -a opts params - declare o alt_recycled=false error=0 - - while [[ $# -gt 0 ]]; do - case $1 in - --) - params=("${params[@]}" "${@:2}") - break - ;; - - --*=*) - o=${1%%=*} - if ! o=$(_getopt_resolve_abbrev "$o" "${longarr[@]}"); then - error=1 - elif [[ ,"$long", == *,"${o#--}"::,* ]]; then - opts=("${opts[@]}" "$o" "${1#*=}") - elif [[ ,"$long", == *,"${o#--}":,* ]]; then - opts=("${opts[@]}" "$o" "${1#*=}") - elif [[ ,"$long", == *,"${o#--}",* ]]; then - if $alt_recycled; then o=${o#-}; fi - _getopt_err "$name: option '$o' doesn't allow an argument" - error=1 - else - echo "getopt: assertion failed (1)" >&2 - return 3 - fi - alt_recycled=false - ;; - - --?*) - o=$1 - if ! o=$(_getopt_resolve_abbrev "$o" "${longarr[@]}"); then - error=1 - elif [[ ,"$long", == *,"${o#--}",* ]]; then - opts=("${opts[@]}" "$o") - elif [[ ,"$long", == *,"${o#--}::",* ]]; then - opts=("${opts[@]}" "$o" '') - elif [[ ,"$long", == *,"${o#--}:",* ]]; then - if [[ $# -ge 2 ]]; then - shift - opts=("${opts[@]}" "$o" "$1") - else - if $alt_recycled; then o=${o#-}; fi - _getopt_err "$name: option '$o' requires an argument" - error=1 - fi - else - echo "getopt: assertion failed (2)" >&2 - return 3 - fi - alt_recycled=false - ;; - - -*) - if [[ $flags == *a* ]]; then - # Alternative parsing mode! - # Try to handle as a long option if any of the following apply: - # 1. There's an equals sign in the mix -x=3 or -xy=3 - # 2. There's 2+ letters and an abbreviated long match -xy - # 3. There's a single letter and an exact long match - # 4. There's a single letter and no short match - o=${1::2} # temp for testing #4 - if [[ $1 == *=* || $1 == -?? || \ - ,$long, == *,"${1#-}"[:,]* || \ - ,$short, != *,"${o#-}"[:,]* ]]; then - o=$(_getopt_resolve_abbrev "${1%%=*}" "${longarr[@]}" 2> /dev/null) - case $? in - 0) - # Unambiguous match. Let the long options parser handle - # it, with a flag to get the right error message. - set -- "-$1" "${@:2}" - alt_recycled=true - continue - ;; - 1) - # Ambiguous match, generate error and continue. - _getopt_resolve_abbrev "${1%%=*}" "${longarr[@]}" > /dev/null - error=1 - shift - continue - ;; - 2) - # No match, fall through to single-character check. - true - ;; - *) - echo "getopt: assertion failed (3)" >&2 - return 3 - ;; - esac - fi - fi - - o=${1::2} - if [[ "$short" == *"${o#-}"::* ]]; then - if [[ ${#1} -gt 2 ]]; then - opts=("${opts[@]}" "$o" "${1:2}") - else - opts=("${opts[@]}" "$o" '') - fi - elif [[ "$short" == *"${o#-}":* ]]; then - if [[ ${#1} -gt 2 ]]; then - opts=("${opts[@]}" "$o" "${1:2}") - elif [[ $# -ge 2 ]]; then - shift - opts=("${opts[@]}" "$o" "$1") - else - _getopt_err "$name: option requires an argument -- '${o#-}'" - error=1 - fi - elif [[ "$short" == *"${o#-}"* ]]; then - opts=("${opts[@]}" "$o") - if [[ ${#1} -gt 2 ]]; then - set -- "$o" "-${1:2}" "${@:2}" - fi - else - if [[ $flags == *a* ]]; then - # Alternative parsing mode! Report on the entire failed - # option. GNU includes =value but we omit it for sanity with - # very long values. - _getopt_err "$name: unrecognized option '${1%%=*}'" - else - _getopt_err "$name: invalid option -- '${o#-}'" - if [[ ${#1} -gt 2 ]]; then - set -- "$o" "-${1:2}" "${@:2}" - fi - fi - error=1 - fi - ;; - - *) - # GNU getopt in-place mode (leading dash on short options) - # overrides POSIXLY_CORRECT - if [[ $flags == *i* ]]; then - opts=("${opts[@]}" "$1") - elif [[ $flags == *p* ]]; then - params=("${params[@]}" "$@") - break - else - params=("${params[@]}" "$1") - fi - ;; - esac - - shift - done - - if [[ $flags == *Q* ]]; then - true # generate no output - else - echo -n ' ' - if [[ $flags == *[cu]* ]]; then - printf '%s -- %s' "${opts[*]}" "${params[*]}" - else - if [[ $flags == *t* ]]; then - _getopt_quote_csh "${opts[@]}" -- "${params[@]}" - else - _getopt_quote "${opts[@]}" -- "${params[@]}" - fi - fi - echo - fi - - return $error - } - - _getopt_err() { - if [[ $flags != *q* ]]; then - printf '%s\n' "$1" >&2 - fi - } - - _getopt_resolve_abbrev() { - # Resolves an abbrevation from a list of possibilities. - # If the abbreviation is unambiguous, echoes the expansion on stdout - # and returns 0. If the abbreviation is ambiguous, prints a message on - # stderr and returns 1. (For first parse this should convert to exit - # status 2.) If there is no match at all, prints a message on stderr - # and returns 2. - declare a q="$1" - declare -a matches - shift - for a; do - if [[ $q == "$a" ]]; then - # Exact match. Squash any other partial matches. - matches=("$a") - break - elif [[ $flags == *a* && $q == -[^-]* && $a == -"$q" ]]; then - # Exact alternative match. Squash any other partial matches. - matches=("$a") - break - elif [[ $a == "$q"* ]]; then - # Abbreviated match. - matches=("${matches[@]}" "$a") - elif [[ $flags == *a* && $q == -[^-]* && $a == -"$q"* ]]; then - # Abbreviated alternative match. - matches=("${matches[@]}" "${a#-}") - fi - done - case ${#matches[@]} in - 0) - [[ $flags == *q* ]] || - printf "$name: unrecognized option %s\\n" \ - "$(_getopt_quote "$q")" >&2 - - return 2 - ;; - 1) - printf '%s' "${matches[0]}" - return 0 - ;; - *) - [[ $flags == *q* ]] || - printf "$name: option %s is ambiguous; possibilities: %s\\n" \ - "$(_getopt_quote "$q")" "$(_getopt_quote "${matches[@]}")" >&2 - - return 1 - ;; - esac - } - - _getopt_split() { - # Splits $2 at commas to build array specified by $1 - declare IFS=, - eval "$1=( \$2 )" - } - - _getopt_quote() { - # Quotes arguments with single quotes, escaping inner single quotes - declare s space q=\' - for s; do - printf "$space'%s'" "${s//$q/$q\\$q$q}" - space=' ' - done - } - - _getopt_quote_csh() { - # Quotes arguments with single quotes, escaping inner single quotes, - # bangs, backslashes and newlines - declare s i c space - for s; do - echo -n "$space'" - for ((i = 0; i < ${#s}; i++)); do - c=${s:i:1} - case $c in - \\ | \' | !) - echo -n "'\\$c'" - ;; - $'\n') - echo -n "\\$c" - ;; - *) - echo -n "$c" - ;; - esac - done - echo -n \' - space=' ' - done - } - - _getopt_help() { - cat <<- EOT >&2 - Usage: - getopt - getopt [options] [--] - getopt [options] -o|--options [options] [--] - Parse command options. - Options: - -a, --alternative allow long options starting with single - - -l, --longoptions the long options to be recognized - -n, --name the name under which errors are reported - -o, --options the short options to be recognized - -q, --quiet disable error reporting by getopt(3) - -Q, --quiet-output no normal output - -s, --shell set quoting conventions to those of - -T, --test test for getopt(1) version - -u, --unquoted do not quote the output - -h, --help display this help and exit - -V, --version output version information and exit - For more details see getopt(1). - EOT - } - - _getopt_version_check() { - if [[ -z $BASH_VERSION ]]; then - echo "getopt: unknown version of bash might not be compatible" >&2 - return 1 - fi - - # This is a lexical comparison that should be sufficient forever. - if [[ $BASH_VERSION < 2.05b ]]; then - echo "getopt: bash $BASH_VERSION might not be compatible" >&2 - return 1 - fi - - return 0 - } - - _getopt_version_check - _getopt_main "$@" - declare status=$? - unset -f _getopt_main _getopt_err _getopt_parse _getopt_quote \ - _getopt_quote_csh _getopt_resolve_abbrev _getopt_split _getopt_help \ - _getopt_version_check - return $status -} +# global arrays +declare -a ARGS +declare -a FILES -[[ $BASH_SOURCE != "$0" ]] || main "$@" +[[ ${BASH_SOURCE[0]} != "$0" ]] || main "$@" diff --git a/terraform_validate.sh b/terraform_validate.sh index 3b44ed77f..d99070aa8 100755 --- a/terraform_validate.sh +++ b/terraform_validate.sh @@ -1,50 +1,115 @@ #!/usr/bin/env bash set -e -declare -a paths -index=0 -error=0 +main() { + initialize_ + parse_cmdline_ "$@" + terraform_validate_ +} -for file_with_path in "$@"; do - file_with_path="${file_with_path// /__REPLACED__SPACE__}" +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")" - paths[index]=$(dirname "$file_with_path") - (("index+=1")) -done + # source getopt function + # shellcheck source=lib_getopt + . "$_SCRIPT_DIR/lib_getopt" +} -for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do - path_uniq="${path_uniq//__REPLACED__SPACE__/ }" +parse_cmdline_() { + declare argv + argv=$(getopt -o e:a: --long envs:,args: -- "$@") || return + eval "set -- $argv" - if [[ -n "$(find $path_uniq -maxdepth 1 -name '*.tf' -print -quit)" ]]; then + for argv; do + case $argv in + -a | --args) + shift + ARGS+=("$1") + shift + ;; + -e | --envs) + shift + ENVS+=("$1") + shift + ;; + --) + shift + FILES=("$@") + break + ;; + esac + done +} - starting_path=$(realpath "$path_uniq") - terraform_path="$path_uniq" +terraform_validate_() { - # Find the relevant .terraform directory (indicating a 'terraform init'), - # but fall through to the current directory. - while [[ "$terraform_path" != "." ]]; do - if [[ -d "$terraform_path/.terraform" ]]; then - break - else - terraform_path=$(dirname "$terraform_path") + # Setup environment variables + for var in "${ENVS[@]}"; do + export "${!var}" + done + + declare -a paths + index=0 + error=0 + + for file_with_path in "${FILES[@]}"; do + file_with_path="${file_with_path// /__REPLACED__SPACE__}" + + paths[index]=$(dirname "$file_with_path") + ((index += 1)) + done + + for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do + path_uniq="${path_uniq//__REPLACED__SPACE__/ }" + + if [[ -n "$(find "$path_uniq" -maxdepth 1 -name '*.tf' -print -quit)" ]]; then + + starting_path=$(realpath "$path_uniq") + terraform_path="$path_uniq" + + # Find the relevant .terraform directory (indicating a 'terraform init'), + # but fall through to the current directory. + while [[ $terraform_path != "." ]]; do + if [[ -d $terraform_path/.terraform ]]; then + break + else + terraform_path=$(dirname "$terraform_path") + fi + done + + validate_path="${path_uniq#"$terraform_path"}" + + # Change to the directory that has been initialized, run validation, then + # change back to the starting directory. + cd "$(realpath "$terraform_path")" + if ! terraform validate "${ARGS[@]}" "$validate_path"; then + error=1 + echo + echo "Failed path: $path_uniq" + echo "================================" fi - done - - validate_path="${path_uniq#"$terraform_path"}" - - # Change to the directory that has been initialized, run validation, then - # change back to the starting directory. - cd "$(realpath "$terraform_path")" - if ! terraform validate $validate_path; then - error=1 - echo - echo "Failed path: $path_uniq" - echo "================================" + cd "$starting_path" fi - cd "$starting_path" + done + + if [[ $error -ne 0 ]]; then + exit 1 fi -done +} + +# global arrays +declare -a ARGS +declare -a ENVS +declare -a FILES -if [[ "${error}" -ne 0 ]]; then - exit 1 -fi +[[ ${BASH_SOURCE[0]} != "$0" ]] || main "$@" From 0c5cbb380bef5c7625c9479de2a08bfc98c3009b Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Thu, 27 Aug 2020 11:58:35 +0200 Subject: [PATCH 082/214] Updated CHANGELOG --- CHANGELOG.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6b31e40a..82c1b701f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ All notable changes to this project will be documented in this file. + +## [v1.33.0] - 2020-08-27 + +- fix: Pass args and env vars to terraform validate ([#125](https://github.com/antonbabenko/pre-commit-terraform/issues/125)) +- docs: Update terraform-docs link pointing to new organization ([#130](https://github.com/antonbabenko/pre-commit-terraform/issues/130)) + + ## [v1.32.0] - 2020-08-19 @@ -297,7 +304,8 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.32.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.33.0...HEAD +[v1.33.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.32.0...v1.33.0 [v1.32.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.31.0...v1.32.0 [v1.31.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.30.0...v1.31.0 [v1.30.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.29.0...v1.30.0 From 1d8af371d43e8ee48ac4b4074244436faa13cb86 Mon Sep 17 00:00:00 2001 From: Robin Bowes Date: Thu, 27 Aug 2020 20:55:28 +0100 Subject: [PATCH 083/214] chore: Use lib_getopt for all hooks and some style tweaks (#137) --- lib_getopt | 12 +- terraform_docs.sh | 584 ++++-------------------------------------- terraform_tfsec.sh | 561 +++------------------------------------- terraform_validate.sh | 17 +- 4 files changed, 99 insertions(+), 1075 deletions(-) diff --git a/lib_getopt b/lib_getopt index e360b490b..c4b21fa80 100644 --- a/lib_getopt +++ b/lib_getopt @@ -2,9 +2,9 @@ getopt() { # pure-getopt, a drop-in replacement for GNU getopt in pure Bash. - # version 1.4.3 + # version 1.4.4 # - # Copyright 2012-2018 Aron Griffis + # Copyright 2012-2020 Aron Griffis # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -37,7 +37,7 @@ getopt() { # "options -- parameters" on stdout. declare parsed status - declare short long name flags + declare short long='' name flags='' declare have_short=false # Synopsis from getopt man-page: @@ -122,7 +122,7 @@ getopt() { return 4 ;; (-V|--version) - echo "pure-getopt 1.4.3" + echo "pure-getopt 1.4.4" return 0 ;; (--) @@ -367,7 +367,7 @@ getopt() { # status 2.) If there is no match at all, prints a message on stderr # and returns 2. declare a q="$1" - declare -a matches + declare -a matches=() shift for a; do if [[ $q == "$a" ]]; then @@ -410,7 +410,7 @@ getopt() { _getopt_quote() { # Quotes arguments with single quotes, escaping inner single quotes - declare s space q=\' + declare s space='' q=\' for s; do printf "$space'%s'" "${s//$q/$q\\$q$q}" space=' ' diff --git a/terraform_docs.sh b/terraform_docs.sh index 8e7393329..e86c910f5 100755 --- a/terraform_docs.sh +++ b/terraform_docs.sh @@ -1,29 +1,54 @@ #!/usr/bin/env bash - -set -e +set -eo pipefail main() { + initialize_ + parse_cmdline_ "$@" + terraform_docs_ "$ARGS" "$FILES" +} + +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_() { declare argv argv=$(getopt -o a: --long args: -- "$@") || return eval "set -- $argv" - declare args - declare files - for argv; do case $argv in -a | --args) shift - args="$1" + ARGS+=("$1") shift ;; --) shift - files="$@" + FILES=("$@") break ;; esac done +} + +terraform_docs_() { + local -r args="$1" + local -r files="$2" local hack_terraform_docs hack_terraform_docs=$(terraform version | head -1 | grep -c 0.12) || true @@ -47,6 +72,7 @@ main() { 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" "$args" "$files" @@ -60,15 +86,15 @@ main() { } terraform_docs() { - readonly terraform_docs_awk_file="$1" - readonly args="$2" - readonly files="$3" + local -r terraform_docs_awk_file="$1" + local -r args="$2" + local -r files="$3" declare -a paths declare -a tfvars_files - index=0 - + local index=0 + local file_with_path for file_with_path in $files; do file_with_path="${file_with_path// /__REPLACED__SPACE__}" @@ -81,9 +107,10 @@ terraform_docs() { ((index += 1)) done - readonly tmp_file=$(mktemp) - readonly text_file="README.md" + local -r tmp_file=$(mktemp) + local -r text_file="README.md" + local path_uniq for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do path_uniq="${path_uniq//__REPLACED__SPACE__/ }" @@ -99,8 +126,10 @@ terraform_docs() { terraform-docs md $args ./ > "$tmp_file" else # 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" @@ -122,7 +151,7 @@ terraform_docs() { } terraform_docs_awk() { - readonly output_file=$1 + local -r output_file=$1 cat << "EOF" > "$output_file" # This script converts Terraform 0.12 variables/outputs to something suitable for `terraform-docs` @@ -280,525 +309,8 @@ EOF } -getopt() { - # pure-getopt, a drop-in replacement for GNU getopt in pure Bash. - # version 1.4.3 - # - # Copyright 2012-2018 Aron Griffis - # - # Permission is hereby granted, free of charge, to any person obtaining - # a copy of this software and associated documentation files (the - # "Software"), to deal in the Software without restriction, including - # without limitation the rights to use, copy, modify, merge, publish, - # distribute, sublicense, and/or sell copies of the Software, and to - # permit persons to whom the Software is furnished to do so, subject to - # the following conditions: - # - # The above copyright notice and this permission notice shall be included - # in all copies or substantial portions of the Software. - # - # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - _getopt_main() { - # Returns one of the following statuses: - # 0 success - # 1 error parsing parameters - # 2 error in getopt invocation - # 3 internal error - # 4 reserved for -T - # - # For statuses 0 and 1, generates normalized and shell-quoted - # "options -- parameters" on stdout. - - declare parsed status - declare short long name flags - declare have_short=false - - # Synopsis from getopt man-page: - # - # getopt optstring parameters - # getopt [options] [--] optstring parameters - # getopt [options] -o|--options optstring [options] [--] parameters - # - # The first form can be normalized to the third form which - # _getopt_parse() understands. The second form can be recognized after - # first parse when $short hasn't been set. - - if [[ -n ${GETOPT_COMPATIBLE+isset} || $1 == [^-]* ]]; then - # Enable compatibility mode - flags=c$flags - # Normalize first to third synopsis form - set -- -o "$1" -- "${@:2}" - fi - - # First parse always uses flags=p since getopt always parses its own - # arguments effectively in this mode. - parsed=$(_getopt_parse getopt ahl:n:o:qQs:TuV \ - alternative,help,longoptions:,name:,options:,quiet,quiet-output,shell:,test,version \ - p "$@") - status=$? - if [[ $status != 0 ]]; then - if [[ $status == 1 ]]; then - echo "Try \`getopt --help' for more information." >&2 - # Since this is the first parse, convert status 1 to 2 - status=2 - fi - return $status - fi - eval "set -- $parsed" - - while [[ $# -gt 0 ]]; do - case $1 in - -a | --alternative) - flags=a$flags - ;; - - -h | --help) - _getopt_help - return 2 # as does GNU getopt - ;; - - -l | --longoptions) - long="$long${long:+,}$2" - shift - ;; - - -n | --name) - name=$2 - shift - ;; - - -o | --options) - short=$2 - have_short=true - shift - ;; - - -q | --quiet) - flags=q$flags - ;; - - -Q | --quiet-output) - flags=Q$flags - ;; - - -s | --shell) - case $2 in - sh | bash) - flags=${flags//t/} - ;; - csh | tcsh) - flags=t$flags - ;; - *) - echo 'getopt: unknown shell after -s or --shell argument' >&2 - echo "Try \`getopt --help' for more information." >&2 - return 2 - ;; - esac - shift - ;; - - -u | --unquoted) - flags=u$flags - ;; - - -T | --test) - return 4 - ;; - - -V | --version) - echo "pure-getopt 1.4.3" - return 0 - ;; - - --) - shift - break - ;; - esac - - shift - done - - if ! $have_short; then - # $short was declared but never set, not even to an empty string. - # This implies the second form in the synopsis. - if [[ $# == 0 ]]; then - echo 'getopt: missing optstring argument' >&2 - echo "Try \`getopt --help' for more information." >&2 - return 2 - fi - short=$1 - have_short=true - shift - fi - - if [[ $short == -* ]]; then - # Leading dash means generate output in place rather than reordering, - # unless we're already in compatibility mode. - [[ $flags == *c* ]] || flags=i$flags - short=${short#?} - elif [[ $short == +* ]]; then - # Leading plus means POSIXLY_CORRECT, unless we're already in - # compatibility mode. - [[ $flags == *c* ]] || flags=p$flags - short=${short#?} - fi - - # This should fire if POSIXLY_CORRECT is in the environment, even if - # it's an empty string. That's the difference between :+ and + - flags=${POSIXLY_CORRECT+p}$flags - - _getopt_parse "${name:-getopt}" "$short" "$long" "$flags" "$@" - } - - _getopt_parse() { - # Inner getopt parser, used for both first parse and second parse. - # Returns 0 for success, 1 for error parsing, 3 for internal error. - # In the case of status 1, still generates stdout with whatever could - # be parsed. - # - # $flags is a string of characters with the following meanings: - # a - alternative parsing mode - # c - GETOPT_COMPATIBLE - # i - generate output in place rather than reordering - # p - POSIXLY_CORRECT - # q - disable error reporting - # Q - disable normal output - # t - quote for csh/tcsh - # u - unquoted output - - declare name="$1" short="$2" long="$3" flags="$4" - shift 4 - - # Split $long on commas, prepend double-dashes, strip colons; - # for use with _getopt_resolve_abbrev - declare -a longarr - _getopt_split longarr "$long" - longarr=("${longarr[@]/#/--}") - longarr=("${longarr[@]%:}") - longarr=("${longarr[@]%:}") - - # Parse and collect options and parameters - declare -a opts params - declare o alt_recycled=false error=0 - - while [[ $# -gt 0 ]]; do - case $1 in - --) - params=("${params[@]}" "${@:2}") - break - ;; - - --*=*) - o=${1%%=*} - if ! o=$(_getopt_resolve_abbrev "$o" "${longarr[@]}"); then - error=1 - elif [[ ,"$long", == *,"${o#--}"::,* ]]; then - opts=("${opts[@]}" "$o" "${1#*=}") - elif [[ ,"$long", == *,"${o#--}":,* ]]; then - opts=("${opts[@]}" "$o" "${1#*=}") - elif [[ ,"$long", == *,"${o#--}",* ]]; then - if $alt_recycled; then o=${o#-}; fi - _getopt_err "$name: option '$o' doesn't allow an argument" - error=1 - else - echo "getopt: assertion failed (1)" >&2 - return 3 - fi - alt_recycled=false - ;; - - --?*) - o=$1 - if ! o=$(_getopt_resolve_abbrev "$o" "${longarr[@]}"); then - error=1 - elif [[ ,"$long", == *,"${o#--}",* ]]; then - opts=("${opts[@]}" "$o") - elif [[ ,"$long", == *,"${o#--}::",* ]]; then - opts=("${opts[@]}" "$o" '') - elif [[ ,"$long", == *,"${o#--}:",* ]]; then - if [[ $# -ge 2 ]]; then - shift - opts=("${opts[@]}" "$o" "$1") - else - if $alt_recycled; then o=${o#-}; fi - _getopt_err "$name: option '$o' requires an argument" - error=1 - fi - else - echo "getopt: assertion failed (2)" >&2 - return 3 - fi - alt_recycled=false - ;; - - -*) - if [[ $flags == *a* ]]; then - # Alternative parsing mode! - # Try to handle as a long option if any of the following apply: - # 1. There's an equals sign in the mix -x=3 or -xy=3 - # 2. There's 2+ letters and an abbreviated long match -xy - # 3. There's a single letter and an exact long match - # 4. There's a single letter and no short match - o=${1::2} # temp for testing #4 - if [[ $1 == *=* || $1 == -?? || \ - ,$long, == *,"${1#-}"[:,]* || \ - ,$short, != *,"${o#-}"[:,]* ]]; then - o=$(_getopt_resolve_abbrev "${1%%=*}" "${longarr[@]}" 2> /dev/null) - case $? in - 0) - # Unambiguous match. Let the long options parser handle - # it, with a flag to get the right error message. - set -- "-$1" "${@:2}" - alt_recycled=true - continue - ;; - 1) - # Ambiguous match, generate error and continue. - _getopt_resolve_abbrev "${1%%=*}" "${longarr[@]}" > /dev/null - error=1 - shift - continue - ;; - 2) - # No match, fall through to single-character check. - true - ;; - *) - echo "getopt: assertion failed (3)" >&2 - return 3 - ;; - esac - fi - fi - - o=${1::2} - if [[ "$short" == *"${o#-}"::* ]]; then - if [[ ${#1} -gt 2 ]]; then - opts=("${opts[@]}" "$o" "${1:2}") - else - opts=("${opts[@]}" "$o" '') - fi - elif [[ "$short" == *"${o#-}":* ]]; then - if [[ ${#1} -gt 2 ]]; then - opts=("${opts[@]}" "$o" "${1:2}") - elif [[ $# -ge 2 ]]; then - shift - opts=("${opts[@]}" "$o" "$1") - else - _getopt_err "$name: option requires an argument -- '${o#-}'" - error=1 - fi - elif [[ "$short" == *"${o#-}"* ]]; then - opts=("${opts[@]}" "$o") - if [[ ${#1} -gt 2 ]]; then - set -- "$o" "-${1:2}" "${@:2}" - fi - else - if [[ $flags == *a* ]]; then - # Alternative parsing mode! Report on the entire failed - # option. GNU includes =value but we omit it for sanity with - # very long values. - _getopt_err "$name: unrecognized option '${1%%=*}'" - else - _getopt_err "$name: invalid option -- '${o#-}'" - if [[ ${#1} -gt 2 ]]; then - set -- "$o" "-${1:2}" "${@:2}" - fi - fi - error=1 - fi - ;; - - *) - # GNU getopt in-place mode (leading dash on short options) - # overrides POSIXLY_CORRECT - if [[ $flags == *i* ]]; then - opts=("${opts[@]}" "$1") - elif [[ $flags == *p* ]]; then - params=("${params[@]}" "$@") - break - else - params=("${params[@]}" "$1") - fi - ;; - esac - - shift - done - - if [[ $flags == *Q* ]]; then - true # generate no output - else - echo -n ' ' - if [[ $flags == *[cu]* ]]; then - printf '%s -- %s' "${opts[*]}" "${params[*]}" - else - if [[ $flags == *t* ]]; then - _getopt_quote_csh "${opts[@]}" -- "${params[@]}" - else - _getopt_quote "${opts[@]}" -- "${params[@]}" - fi - fi - echo - fi - - return $error - } - - _getopt_err() { - if [[ $flags != *q* ]]; then - printf '%s\n' "$1" >&2 - fi - } - - _getopt_resolve_abbrev() { - # Resolves an abbrevation from a list of possibilities. - # If the abbreviation is unambiguous, echoes the expansion on stdout - # and returns 0. If the abbreviation is ambiguous, prints a message on - # stderr and returns 1. (For first parse this should convert to exit - # status 2.) If there is no match at all, prints a message on stderr - # and returns 2. - declare a q="$1" - declare -a matches - shift - for a; do - if [[ $q == "$a" ]]; then - # Exact match. Squash any other partial matches. - matches=("$a") - break - elif [[ $flags == *a* && $q == -[^-]* && $a == -"$q" ]]; then - # Exact alternative match. Squash any other partial matches. - matches=("$a") - break - elif [[ $a == "$q"* ]]; then - # Abbreviated match. - matches=("${matches[@]}" "$a") - elif [[ $flags == *a* && $q == -[^-]* && $a == -"$q"* ]]; then - # Abbreviated alternative match. - matches=("${matches[@]}" "${a#-}") - fi - done - case ${#matches[@]} in - 0) - [[ $flags == *q* ]] || - printf "$name: unrecognized option %s\\n" \ - "$(_getopt_quote "$q")" >&2 - - return 2 - ;; - 1) - printf '%s' "${matches[0]}" - return 0 - ;; - *) - [[ $flags == *q* ]] || - printf "$name: option %s is ambiguous; possibilities: %s\\n" \ - "$(_getopt_quote "$q")" "$(_getopt_quote "${matches[@]}")" >&2 - - return 1 - ;; - esac - } - - _getopt_split() { - # Splits $2 at commas to build array specified by $1 - declare IFS=, - eval "$1=( \$2 )" - } - - _getopt_quote() { - # Quotes arguments with single quotes, escaping inner single quotes - declare s space q=\' - for s; do - printf "$space'%s'" "${s//$q/$q\\$q$q}" - space=' ' - done - } - - _getopt_quote_csh() { - # Quotes arguments with single quotes, escaping inner single quotes, - # bangs, backslashes and newlines - declare s i c space - for s; do - echo -n "$space'" - for ((i = 0; i < ${#s}; i++)); do - c=${s:i:1} - case $c in - \\ | \' | !) - echo -n "'\\$c'" - ;; - $'\n') - echo -n "\\$c" - ;; - *) - echo -n "$c" - ;; - esac - done - echo -n \' - space=' ' - done - } - - _getopt_help() { - cat <<- EOT >&2 - - Usage: - getopt - getopt [options] [--] - getopt [options] -o|--options [options] [--] - - Parse command options. - - Options: - -a, --alternative allow long options starting with single - - -l, --longoptions the long options to be recognized - -n, --name the name under which errors are reported - -o, --options the short options to be recognized - -q, --quiet disable error reporting by getopt(3) - -Q, --quiet-output no normal output - -s, --shell set quoting conventions to those of - -T, --test test for getopt(1) version - -u, --unquoted do not quote the output - - -h, --help display this help and exit - -V, --version output version information and exit - - For more details see getopt(1). - EOT - } - - _getopt_version_check() { - if [[ -z $BASH_VERSION ]]; then - echo "getopt: unknown version of bash might not be compatible" >&2 - return 1 - fi - - # This is a lexical comparison that should be sufficient forever. - if [[ $BASH_VERSION < 2.05b ]]; then - echo "getopt: bash $BASH_VERSION might not be compatible" >&2 - return 1 - fi - - return 0 - } - - _getopt_version_check - _getopt_main "$@" - declare status=$? - unset -f _getopt_main _getopt_err _getopt_parse _getopt_quote \ - _getopt_quote_csh _getopt_resolve_abbrev _getopt_split _getopt_help \ - _getopt_version_check - return $status -} +# global arrays +declare -a ARGS=() +declare -a FILES=() -[[ $BASH_SOURCE != "$0" ]] || main "$@" +[[ ${BASH_SOURCE[0]} != "$0" ]] || main "$@" diff --git a/terraform_tfsec.sh b/terraform_tfsec.sh index 78aaf0945..e063de1fc 100755 --- a/terraform_tfsec.sh +++ b/terraform_tfsec.sh @@ -1,551 +1,54 @@ #!/usr/bin/env bash -set -e +set -eo pipefail main() { + initialize_ + parse_cmdline_ "$@" + + # Don't pass any files tfsec will recurse directories anyway. + tfsec "$ARGS" . +} + +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_() { declare argv argv=$(getopt -o a: --long args: -- "$@") || return eval "set -- $argv" - declare args - declare files - for argv; do case $argv in -a | --args) shift - args="$1" + ARGS+=("$1") shift ;; --) shift - files="$@" + # ignore any parameters, as they're not used break ;; esac done - - tfsec_ "$args" "$files" } -tfsec_() { - # Ignore $files because tfsec will recurse directories anyway. - tfsec $args . -} - -getopt() { - # pure-getopt, a drop-in replacement for GNU getopt in pure Bash. - # version 1.4.3 - # - # Copyright 2012-2018 Aron Griffis - # - # Permission is hereby granted, free of charge, to any person obtaining - # a copy of this software and associated documentation files (the - # "Software"), to deal in the Software without restriction, including - # without limitation the rights to use, copy, modify, merge, publish, - # distribute, sublicense, and/or sell copies of the Software, and to - # permit persons to whom the Software is furnished to do so, subject to - # the following conditions: - # - # The above copyright notice and this permission notice shall be included - # in all copies or substantial portions of the Software. - # - # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - _getopt_main() { - # Returns one of the following statuses: - # 0 success - # 1 error parsing parameters - # 2 error in getopt invocation - # 3 internal error - # 4 reserved for -T - # - # For statuses 0 and 1, generates normalized and shell-quoted - # "options -- parameters" on stdout. - - declare parsed status - declare short long name flags - declare have_short=false - - # Synopsis from getopt man-page: - # - # getopt optstring parameters - # getopt [options] [--] optstring parameters - # getopt [options] -o|--options optstring [options] [--] parameters - # - # The first form can be normalized to the third form which - # _getopt_parse() understands. The second form can be recognized after - # first parse when $short hasn't been set. - - if [[ -n ${GETOPT_COMPATIBLE+isset} || $1 == [^-]* ]]; then - # Enable compatibility mode - flags=c$flags - # Normalize first to third synopsis form - set -- -o "$1" -- "${@:2}" - fi - - # First parse always uses flags=p since getopt always parses its own - # arguments effectively in this mode. - parsed=$(_getopt_parse getopt ahl:n:o:qQs:TuV \ - alternative,help,longoptions:,name:,options:,quiet,quiet-output,shell:,test,version \ - p "$@") - status=$? - if [[ $status != 0 ]]; then - if [[ $status == 1 ]]; then - echo "Try \`getopt --help' for more information." >&2 - # Since this is the first parse, convert status 1 to 2 - status=2 - fi - return $status - fi - eval "set -- $parsed" - - while [[ $# -gt 0 ]]; do - case $1 in - -a | --alternative) - flags=a$flags - ;; - - -h | --help) - _getopt_help - return 2 # as does GNU getopt - ;; - - -l | --longoptions) - long="$long${long:+,}$2" - shift - ;; - - -n | --name) - name=$2 - shift - ;; - - -o | --options) - short=$2 - have_short=true - shift - ;; - - -q | --quiet) - flags=q$flags - ;; - - -Q | --quiet-output) - flags=Q$flags - ;; - - -s | --shell) - case $2 in - sh | bash) - flags=${flags//t/} - ;; - csh | tcsh) - flags=t$flags - ;; - *) - echo 'getopt: unknown shell after -s or --shell argument' >&2 - echo "Try \`getopt --help' for more information." >&2 - return 2 - ;; - esac - shift - ;; - - -u | --unquoted) - flags=u$flags - ;; - - -T | --test) - return 4 - ;; - - -V | --version) - echo "pure-getopt 1.4.3" - return 0 - ;; - - --) - shift - break - ;; - esac - - shift - done - - if ! $have_short; then - # $short was declared but never set, not even to an empty string. - # This implies the second form in the synopsis. - if [[ $# == 0 ]]; then - echo 'getopt: missing optstring argument' >&2 - echo "Try \`getopt --help' for more information." >&2 - return 2 - fi - short=$1 - have_short=true - shift - fi - - if [[ $short == -* ]]; then - # Leading dash means generate output in place rather than reordering, - # unless we're already in compatibility mode. - [[ $flags == *c* ]] || flags=i$flags - short=${short#?} - elif [[ $short == +* ]]; then - # Leading plus means POSIXLY_CORRECT, unless we're already in - # compatibility mode. - [[ $flags == *c* ]] || flags=p$flags - short=${short#?} - fi - - # This should fire if POSIXLY_CORRECT is in the environment, even if - # it's an empty string. That's the difference between :+ and + - flags=${POSIXLY_CORRECT+p}$flags - - _getopt_parse "${name:-getopt}" "$short" "$long" "$flags" "$@" - } - - _getopt_parse() { - # Inner getopt parser, used for both first parse and second parse. - # Returns 0 for success, 1 for error parsing, 3 for internal error. - # In the case of status 1, still generates stdout with whatever could - # be parsed. - # - # $flags is a string of characters with the following meanings: - # a - alternative parsing mode - # c - GETOPT_COMPATIBLE - # i - generate output in place rather than reordering - # p - POSIXLY_CORRECT - # q - disable error reporting - # Q - disable normal output - # t - quote for csh/tcsh - # u - unquoted output - - declare name="$1" short="$2" long="$3" flags="$4" - shift 4 - - # Split $long on commas, prepend double-dashes, strip colons; - # for use with _getopt_resolve_abbrev - declare -a longarr - _getopt_split longarr "$long" - longarr=("${longarr[@]/#/--}") - longarr=("${longarr[@]%:}") - longarr=("${longarr[@]%:}") - - # Parse and collect options and parameters - declare -a opts params - declare o alt_recycled=false error=0 - - while [[ $# -gt 0 ]]; do - case $1 in - --) - params=("${params[@]}" "${@:2}") - break - ;; - - --*=*) - o=${1%%=*} - if ! o=$(_getopt_resolve_abbrev "$o" "${longarr[@]}"); then - error=1 - elif [[ ,"$long", == *,"${o#--}"::,* ]]; then - opts=("${opts[@]}" "$o" "${1#*=}") - elif [[ ,"$long", == *,"${o#--}":,* ]]; then - opts=("${opts[@]}" "$o" "${1#*=}") - elif [[ ,"$long", == *,"${o#--}",* ]]; then - if $alt_recycled; then o=${o#-}; fi - _getopt_err "$name: option '$o' doesn't allow an argument" - error=1 - else - echo "getopt: assertion failed (1)" >&2 - return 3 - fi - alt_recycled=false - ;; - - --?*) - o=$1 - if ! o=$(_getopt_resolve_abbrev "$o" "${longarr[@]}"); then - error=1 - elif [[ ,"$long", == *,"${o#--}",* ]]; then - opts=("${opts[@]}" "$o") - elif [[ ,"$long", == *,"${o#--}::",* ]]; then - opts=("${opts[@]}" "$o" '') - elif [[ ,"$long", == *,"${o#--}:",* ]]; then - if [[ $# -ge 2 ]]; then - shift - opts=("${opts[@]}" "$o" "$1") - else - if $alt_recycled; then o=${o#-}; fi - _getopt_err "$name: option '$o' requires an argument" - error=1 - fi - else - echo "getopt: assertion failed (2)" >&2 - return 3 - fi - alt_recycled=false - ;; - - -*) - if [[ $flags == *a* ]]; then - # Alternative parsing mode! - # Try to handle as a long option if any of the following apply: - # 1. There's an equals sign in the mix -x=3 or -xy=3 - # 2. There's 2+ letters and an abbreviated long match -xy - # 3. There's a single letter and an exact long match - # 4. There's a single letter and no short match - o=${1::2} # temp for testing #4 - if [[ $1 == *=* || $1 == -?? || \ - ,$long, == *,"${1#-}"[:,]* || \ - ,$short, != *,"${o#-}"[:,]* ]]; then - o=$(_getopt_resolve_abbrev "${1%%=*}" "${longarr[@]}" 2> /dev/null) - case $? in - 0) - # Unambiguous match. Let the long options parser handle - # it, with a flag to get the right error message. - set -- "-$1" "${@:2}" - alt_recycled=true - continue - ;; - 1) - # Ambiguous match, generate error and continue. - _getopt_resolve_abbrev "${1%%=*}" "${longarr[@]}" > /dev/null - error=1 - shift - continue - ;; - 2) - # No match, fall through to single-character check. - true - ;; - *) - echo "getopt: assertion failed (3)" >&2 - return 3 - ;; - esac - fi - fi - - o=${1::2} - if [[ "$short" == *"${o#-}"::* ]]; then - if [[ ${#1} -gt 2 ]]; then - opts=("${opts[@]}" "$o" "${1:2}") - else - opts=("${opts[@]}" "$o" '') - fi - elif [[ "$short" == *"${o#-}":* ]]; then - if [[ ${#1} -gt 2 ]]; then - opts=("${opts[@]}" "$o" "${1:2}") - elif [[ $# -ge 2 ]]; then - shift - opts=("${opts[@]}" "$o" "$1") - else - _getopt_err "$name: option requires an argument -- '${o#-}'" - error=1 - fi - elif [[ "$short" == *"${o#-}"* ]]; then - opts=("${opts[@]}" "$o") - if [[ ${#1} -gt 2 ]]; then - set -- "$o" "-${1:2}" "${@:2}" - fi - else - if [[ $flags == *a* ]]; then - # Alternative parsing mode! Report on the entire failed - # option. GNU includes =value but we omit it for sanity with - # very long values. - _getopt_err "$name: unrecognized option '${1%%=*}'" - else - _getopt_err "$name: invalid option -- '${o#-}'" - if [[ ${#1} -gt 2 ]]; then - set -- "$o" "-${1:2}" "${@:2}" - fi - fi - error=1 - fi - ;; - - *) - # GNU getopt in-place mode (leading dash on short options) - # overrides POSIXLY_CORRECT - if [[ $flags == *i* ]]; then - opts=("${opts[@]}" "$1") - elif [[ $flags == *p* ]]; then - params=("${params[@]}" "$@") - break - else - params=("${params[@]}" "$1") - fi - ;; - esac - - shift - done - - if [[ $flags == *Q* ]]; then - true # generate no output - else - echo -n ' ' - if [[ $flags == *[cu]* ]]; then - printf '%s -- %s' "${opts[*]}" "${params[*]}" - else - if [[ $flags == *t* ]]; then - _getopt_quote_csh "${opts[@]}" -- "${params[@]}" - else - _getopt_quote "${opts[@]}" -- "${params[@]}" - fi - fi - echo - fi - - return $error - } - - _getopt_err() { - if [[ $flags != *q* ]]; then - printf '%s\n' "$1" >&2 - fi - } - - _getopt_resolve_abbrev() { - # Resolves an abbrevation from a list of possibilities. - # If the abbreviation is unambiguous, echoes the expansion on stdout - # and returns 0. If the abbreviation is ambiguous, prints a message on - # stderr and returns 1. (For first parse this should convert to exit - # status 2.) If there is no match at all, prints a message on stderr - # and returns 2. - declare a q="$1" - declare -a matches - shift - for a; do - if [[ $q == "$a" ]]; then - # Exact match. Squash any other partial matches. - matches=("$a") - break - elif [[ $flags == *a* && $q == -[^-]* && $a == -"$q" ]]; then - # Exact alternative match. Squash any other partial matches. - matches=("$a") - break - elif [[ $a == "$q"* ]]; then - # Abbreviated match. - matches=("${matches[@]}" "$a") - elif [[ $flags == *a* && $q == -[^-]* && $a == -"$q"* ]]; then - # Abbreviated alternative match. - matches=("${matches[@]}" "${a#-}") - fi - done - case ${#matches[@]} in - 0) - [[ $flags == *q* ]] || - printf "$name: unrecognized option %s\\n" \ - "$(_getopt_quote "$q")" >&2 - - return 2 - ;; - 1) - printf '%s' "${matches[0]}" - return 0 - ;; - *) - [[ $flags == *q* ]] || - printf "$name: option %s is ambiguous; possibilities: %s\\n" \ - "$(_getopt_quote "$q")" "$(_getopt_quote "${matches[@]}")" >&2 - - return 1 - ;; - esac - } - - _getopt_split() { - # Splits $2 at commas to build array specified by $1 - declare IFS=, - eval "$1=( \$2 )" - } - - _getopt_quote() { - # Quotes arguments with single quotes, escaping inner single quotes - declare s space q=\' - for s; do - printf "$space'%s'" "${s//$q/$q\\$q$q}" - space=' ' - done - } - - _getopt_quote_csh() { - # Quotes arguments with single quotes, escaping inner single quotes, - # bangs, backslashes and newlines - declare s i c space - for s; do - echo -n "$space'" - for ((i = 0; i < ${#s}; i++)); do - c=${s:i:1} - case $c in - \\ | \' | !) - echo -n "'\\$c'" - ;; - $'\n') - echo -n "\\$c" - ;; - *) - echo -n "$c" - ;; - esac - done - echo -n \' - space=' ' - done - } - - _getopt_help() { - cat <<- EOT >&2 - Usage: - getopt - getopt [options] [--] - getopt [options] -o|--options [options] [--] - Parse command options. - Options: - -a, --alternative allow long options starting with single - - -l, --longoptions the long options to be recognized - -n, --name the name under which errors are reported - -o, --options the short options to be recognized - -q, --quiet disable error reporting by getopt(3) - -Q, --quiet-output no normal output - -s, --shell set quoting conventions to those of - -T, --test test for getopt(1) version - -u, --unquoted do not quote the output - -h, --help display this help and exit - -V, --version output version information and exit - For more details see getopt(1). - EOT - } - - _getopt_version_check() { - if [[ -z $BASH_VERSION ]]; then - echo "getopt: unknown version of bash might not be compatible" >&2 - return 1 - fi - - # This is a lexical comparison that should be sufficient forever. - if [[ $BASH_VERSION < 2.05b ]]; then - echo "getopt: bash $BASH_VERSION might not be compatible" >&2 - return 1 - fi - - return 0 - } - - _getopt_version_check - _getopt_main "$@" - declare status=$? - unset -f _getopt_main _getopt_err _getopt_parse _getopt_quote \ - _getopt_quote_csh _getopt_resolve_abbrev _getopt_split _getopt_help \ - _getopt_version_check - return $status -} +# global arrays +declare -a ARGS=() -[[ $BASH_SOURCE != "$0" ]] || main "$@" +[[ ${BASH_SOURCE[0]} != "$0" ]] || main "$@" diff --git a/terraform_validate.sh b/terraform_validate.sh index d99070aa8..c224fa88a 100755 --- a/terraform_validate.sh +++ b/terraform_validate.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -set -e +set -eo pipefail main() { initialize_ @@ -54,14 +54,19 @@ parse_cmdline_() { terraform_validate_() { # Setup environment variables + local var var_name var_value for var in "${ENVS[@]}"; do - export "${!var}" + var_name="${var%%=*}" + var_value="${var#*=}" + # shellcheck disable=SC2086 + export $var_name="$var_value" done declare -a paths - index=0 - error=0 + local index=0 + local error=0 + local file_with_path for file_with_path in "${FILES[@]}"; do file_with_path="${file_with_path// /__REPLACED__SPACE__}" @@ -69,12 +74,15 @@ terraform_validate_() { ((index += 1)) done + local path_uniq for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do path_uniq="${path_uniq//__REPLACED__SPACE__/ }" if [[ -n "$(find "$path_uniq" -maxdepth 1 -name '*.tf' -print -quit)" ]]; then + local starting_path starting_path=$(realpath "$path_uniq") + local terraform_path terraform_path="$path_uniq" # Find the relevant .terraform directory (indicating a 'terraform init'), @@ -87,6 +95,7 @@ terraform_validate_() { fi done + local validate_path validate_path="${path_uniq#"$terraform_path"}" # Change to the directory that has been initialized, run validation, then From 861c1166f9a1cf1a9ace8e81c38ffed1902e20d9 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Thu, 27 Aug 2020 21:56:48 +0200 Subject: [PATCH 084/214] Updated CHANGELOG --- CHANGELOG.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 82c1b701f..3af01bec2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ All notable changes to this project will be documented in this file. + +## [v1.34.0] - 2020-08-27 + +- chore: Use lib_getopt for all hooks and some style tweaks ([#137](https://github.com/antonbabenko/pre-commit-terraform/issues/137)) + + ## [v1.33.0] - 2020-08-27 @@ -304,7 +310,8 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.33.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.34.0...HEAD +[v1.34.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.33.0...v1.34.0 [v1.33.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.32.0...v1.33.0 [v1.32.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.31.0...v1.32.0 [v1.31.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.30.0...v1.31.0 From 6c77a6cdc9ff00af50ca7b435ca8ecfa0f7bee1d Mon Sep 17 00:00:00 2001 From: Robin Bowes Date: Fri, 28 Aug 2020 08:03:38 +0100 Subject: [PATCH 085/214] fix: Squash terraform_docs bug (#138) --- terraform_docs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform_docs.sh b/terraform_docs.sh index e86c910f5..52242a5e2 100755 --- a/terraform_docs.sh +++ b/terraform_docs.sh @@ -59,7 +59,7 @@ terraform_docs_() { fi local is_old_terraform_docs - is_old_terraform_docs=$(terraform-docs version | grep -o "v0.[1-7]" | tail -1) + 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) From 266906591c92a974b30cbd5cb1f6f562a8e33594 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Fri, 28 Aug 2020 09:03:59 +0200 Subject: [PATCH 086/214] Updated CHANGELOG --- CHANGELOG.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3af01bec2..0b5db6e60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ All notable changes to this project will be documented in this file. + +## [v1.35.0] - 2020-08-28 + +- fix: Squash terraform_docs bug ([#138](https://github.com/antonbabenko/pre-commit-terraform/issues/138)) + + ## [v1.34.0] - 2020-08-27 @@ -310,7 +316,8 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.34.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.35.0...HEAD +[v1.35.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.34.0...v1.35.0 [v1.34.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.33.0...v1.34.0 [v1.33.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.32.0...v1.33.0 [v1.32.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.31.0...v1.32.0 From 108c75f9798a9cc8a7ecefcf8924cb50edea0ff4 Mon Sep 17 00:00:00 2001 From: nkazarian-spokeo <51686594+nkazarian-spokeo@users.noreply.github.com> Date: Tue, 1 Sep 2020 01:07:08 -0700 Subject: [PATCH 087/214] feat: have option for terraform_tfsec hook to only run in relevant modified directories (#135) --- README.md | 17 ++++++++++++++++- terraform_tfsec.sh | 25 ++++++++++++++++++++++--- 2 files changed, 38 insertions(+), 4 deletions(-) mode change 100755 => 100644 terraform_tfsec.sh diff --git a/README.md b/README.md index 4fce40757..524f4ee66 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,22 @@ if they are present in `README.md`. ## Notes about terraform_tfsec hooks -1. `terraform_tfsec` will recurse all directories/modules. +1. `terraform_tfsec` will consume modified files that pre-commit + passes to it, so you can perform whitelisting of directories + or files to run against via [files](https://pre-commit.com/#config-files) + pre-commit flag + + 1. Example: + ```yaml + hooks: + - id: terraform_tfsec + files: ^prd-infra/ + ``` + + The above will tell pre-commit to pass down files from the `prd-infra/` folder + only such that the underlying `tfsec` tool can run against changed files in this + directory, ignoring any other folders at the root level + 1. To ignore specific warnings, follow the convention from the [documentation](https://github.com/liamg/tfsec#ignoring-warnings). 1. Example: diff --git a/terraform_tfsec.sh b/terraform_tfsec.sh old mode 100755 new mode 100644 index e063de1fc..a698420c6 --- a/terraform_tfsec.sh +++ b/terraform_tfsec.sh @@ -5,8 +5,26 @@ main() { initialize_ parse_cmdline_ "$@" - # Don't pass any files tfsec will recurse directories anyway. - tfsec "$ARGS" . + # propagate $FILES to custom function + tfsec_ "$ARGS" "$FILES" +} + +tfsec_() { + # consume modified files passed from pre-commit so that + # tfsec runs against only those relevant directories + for file_with_path in $FILES; do + file_with_path="${file_with_path// /__REPLACED__SPACE__}" + paths[index]=$(dirname "$file_with_path") + + let "index+=1" + done + + for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do + path_uniq="${path_uniq//__REPLACED__SPACE__/ }" + pushd "$path_uniq" > /dev/null + tfsec $ARGS + popd > /dev/null + done } initialize_() { @@ -41,7 +59,7 @@ parse_cmdline_() { ;; --) shift - # ignore any parameters, as they're not used + FILES+=("$@") break ;; esac @@ -50,5 +68,6 @@ parse_cmdline_() { # global arrays declare -a ARGS=() +declare -a FILES=() [[ ${BASH_SOURCE[0]} != "$0" ]] || main "$@" From c8c25ec064bd064ca087d52d2b9ead68b6f58c7a Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Tue, 1 Sep 2020 10:09:26 +0200 Subject: [PATCH 088/214] Updated CHANGELOG --- CHANGELOG.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b5db6e60..91ea0c8bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ All notable changes to this project will be documented in this file. + +## [v1.36.0] - 2020-09-01 + +- feat: have option for terraform_tfsec hook to only run in relevant modified directories ([#135](https://github.com/antonbabenko/pre-commit-terraform/issues/135)) + + ## [v1.35.0] - 2020-08-28 @@ -316,7 +322,8 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.35.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.36.0...HEAD +[v1.36.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.35.0...v1.36.0 [v1.35.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.34.0...v1.35.0 [v1.34.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.33.0...v1.34.0 [v1.33.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.32.0...v1.33.0 From 077c423cc5c8babe13e394cd3e73c86ceefc5191 Mon Sep 17 00:00:00 2001 From: nkazarian-spokeo <51686594+nkazarian-spokeo@users.noreply.github.com> Date: Tue, 1 Sep 2020 11:45:36 -0700 Subject: [PATCH 089/214] fix: make terraform_tfsec.sh executable (#140) --- terraform_tfsec.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 terraform_tfsec.sh diff --git a/terraform_tfsec.sh b/terraform_tfsec.sh old mode 100644 new mode 100755 From f2e3a5f796d7a0d8436b18598e756da528654dcc Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Tue, 1 Sep 2020 20:46:37 +0200 Subject: [PATCH 090/214] Updated CHANGELOG --- CHANGELOG.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91ea0c8bd..19f08dfb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ All notable changes to this project will be documented in this file. + +## [v1.37.0] - 2020-09-01 + +- fix: make terraform_tfsec.sh executable ([#140](https://github.com/antonbabenko/pre-commit-terraform/issues/140)) + + ## [v1.36.0] - 2020-09-01 @@ -322,7 +328,8 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.36.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.37.0...HEAD +[v1.37.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.36.0...v1.37.0 [v1.36.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.35.0...v1.36.0 [v1.35.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.34.0...v1.35.0 [v1.34.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.33.0...v1.34.0 From f2cab31bc41e913ea63b92b6fc0b3b6e2a2c039a Mon Sep 17 00:00:00 2001 From: Sergei Ivanov Date: Mon, 7 Sep 2020 14:50:57 +0100 Subject: [PATCH 091/214] fix: Correctly handle arrays in terraform_docs.sh (#141) --- .pre-commit-config.yaml | 4 ++-- terraform_docs.sh | 16 +++++++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9b4a49475..90e69ba8f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: git://github.com/pre-commit/pre-commit-hooks - rev: v2.5.0 + rev: v3.2.0 hooks: - id: check-yaml - id: end-of-file-fixer @@ -9,7 +9,7 @@ repos: - id: check-merge-conflict - id: check-executables-have-shebangs - repo: git://github.com/jumanjihouse/pre-commit-hooks - rev: 2.0.0 + rev: 2.1.4 hooks: - id: shfmt args: ['-l', '-i', '2', '-ci', '-sr', '-w'] diff --git a/terraform_docs.sh b/terraform_docs.sh index 52242a5e2..ae898bcd3 100755 --- a/terraform_docs.sh +++ b/terraform_docs.sh @@ -4,7 +4,7 @@ set -eo pipefail main() { initialize_ parse_cmdline_ "$@" - terraform_docs_ "$ARGS" "$FILES" + terraform_docs_ "${ARGS[*]}" "${FILES[@]}" } initialize_() { @@ -48,7 +48,8 @@ parse_cmdline_() { terraform_docs_() { local -r args="$1" - local -r files="$2" + shift + local -a -r files=("$@") local hack_terraform_docs hack_terraform_docs=$(terraform version | head -1 | grep -c 0.12) || true @@ -63,7 +64,7 @@ terraform_docs_() { if [[ -z "$is_old_terraform_docs" ]]; then # Using terraform-docs 0.8+ (preferred) - terraform_docs "0" "$args" "$files" + terraform_docs "0" "$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 @@ -75,12 +76,12 @@ terraform_docs_() { local tmp_file_awk tmp_file_awk=$(mktemp "${TMPDIR:-/tmp}/terraform-docs-XXXXXXXXXX") terraform_docs_awk "$tmp_file_awk" - terraform_docs "$tmp_file_awk" "$args" "$files" + terraform_docs "$tmp_file_awk" "$args" "${files[@]}" rm -f "$tmp_file_awk" else # Using terraform 0.11 and no awk script is needed for that - terraform_docs "0" "$args" "$files" + terraform_docs "0" "$args" "${files[@]}" fi } @@ -88,14 +89,15 @@ terraform_docs_() { terraform_docs() { local -r terraform_docs_awk_file="$1" local -r args="$2" - local -r files="$3" + shift 2 + local -a -r files=("$@") declare -a paths declare -a tfvars_files local index=0 local file_with_path - for file_with_path in $files; do + for file_with_path in "${files[@]}"; do file_with_path="${file_with_path// /__REPLACED__SPACE__}" paths[index]=$(dirname "$file_with_path") From 45e16de525ef5a48dc3d30b180214c07468571b8 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Mon, 7 Sep 2020 16:04:03 +0200 Subject: [PATCH 092/214] Updated CHANGELOG --- CHANGELOG.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19f08dfb9..3e66c9893 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ All notable changes to this project will be documented in this file. + +## [v1.38.0] - 2020-09-07 + +- fix: Correctly handle arrays in terraform_docs.sh ([#141](https://github.com/antonbabenko/pre-commit-terraform/issues/141)) + + ## [v1.37.0] - 2020-09-01 @@ -328,7 +334,8 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.37.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.38.0...HEAD +[v1.38.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.37.0...v1.38.0 [v1.37.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.36.0...v1.37.0 [v1.36.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.35.0...v1.36.0 [v1.35.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.34.0...v1.35.0 From 293b64c0eaa38bcdd79a5576243bc2949378ff32 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Tue, 8 Sep 2020 15:10:56 +0200 Subject: [PATCH 093/214] feat: Add checkov support (#143) --- .pre-commit-hooks.yaml | 11 +++++++++++ README.md | 2 ++ 2 files changed, 13 insertions(+) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 94fe7d432..5763b110c 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -71,3 +71,14 @@ description: Static analysis of Terraform templates to spot potential security issues. entry: terraform_tfsec.sh language: script + +- id: checkov + name: Checkov + description: Runs checkov on Terraform templates. + entry: checkov -d . + language: python + pass_filenames: false + always_run: false + files: \.tf$ + exclude: \.+.terraform\/.*$ + require_serial: true diff --git a/README.md b/README.md index 524f4ee66..53d14b916 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ * [`TFLint`](https://github.com/terraform-linters/tflint) required for `terraform_tflint` hook. * [`TFSec`](https://github.com/liamg/tfsec) required for `terraform_tfsec` hook. * [`coreutils`](https://formulae.brew.sh/formula/coreutils) required for `terraform_validate` hook on macOS (due to use of `realpath`). +* [`checkov`](https://github.com/bridgecrewio/checkov) required for `checkov` hook. ##### MacOS @@ -76,6 +77,7 @@ There are several [pre-commit](https://pre-commit.com/) hooks to keep Terraform | `terragrunt_fmt` | Rewrites all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) to a canonical format. | | `terragrunt_validate` | Validates all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) | | `terraform_tfsec` | [TFSec](https://github.com/liamg/tfsec) static analysis of terraform templates to spot potential security issues. | +| `checkov` | [checkov](https://github.com/bridgecrewio/checkov) static analysis of terraform templates to spot potential security issues. | Check the [source file](https://github.com/antonbabenko/pre-commit-terraform/blob/master/.pre-commit-hooks.yaml) to know arguments used for each hook. From e4ab41045db17eb0e89a87618ffd9482c4818330 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Tue, 8 Sep 2020 15:11:27 +0200 Subject: [PATCH 094/214] Updated CHANGELOG --- CHANGELOG.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e66c9893..6d833497c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ All notable changes to this project will be documented in this file. + +## [v1.39.0] - 2020-09-08 + +- feat: Add checkov support ([#143](https://github.com/antonbabenko/pre-commit-terraform/issues/143)) + + ## [v1.38.0] - 2020-09-07 @@ -334,7 +340,8 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.38.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.39.0...HEAD +[v1.39.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.38.0...v1.39.0 [v1.38.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.37.0...v1.38.0 [v1.37.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.36.0...v1.37.0 [v1.36.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.35.0...v1.36.0 From cf07b5ea3ff76d82f9352aad6dc37e43aa769f9d Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Tue, 22 Sep 2020 14:20:27 +0200 Subject: [PATCH 095/214] feat: Add possibility to share tflint config file for subdirs (#149) --- README.md | 10 ++++++++++ terraform_tflint.sh | 6 ++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 53d14b916..c54a5ec56 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,16 @@ if they are present in `README.md`. - '--args=--enable-rule=terraform_documented_variables' ``` +1. When you have multiple directories and want to run `tflint` in all of them and share single config file it is impractical to hard-code the path to `.tflint.hcl` file. The solution is to use `__GIT_WORKING_DIR__` placeholder which will be replaced by `terraform_tflint` hooks with Git working directory (repo root) at run time. For example: + + ```yaml + hooks: + - id: terraform_tflint + args: + - '--args=--config=__GIT_WORKING_DIR__/.tflint.hcl' + ``` + + ## Notes about terraform_tfsec hooks 1. `terraform_tfsec` will consume modified files that pre-commit diff --git a/terraform_tflint.sh b/terraform_tflint.sh index e7a212b89..670e860ad 100755 --- a/terraform_tflint.sh +++ b/terraform_tflint.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -set -e +set -eo pipefail main() { initialize_ @@ -35,7 +35,8 @@ parse_cmdline_() { case $argv in -a | --args) shift - ARGS+=("$1") + expanded_arg="${1//__GIT_WORKING_DIR__/$PWD}" + ARGS+=("$expanded_arg") shift ;; --) @@ -45,6 +46,7 @@ parse_cmdline_() { ;; esac done + } tflint_() { From 4a611121b6655127d8f94afca34cf668c81c4ae9 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Tue, 22 Sep 2020 14:22:58 +0200 Subject: [PATCH 096/214] Updated CHANGELOG --- CHANGELOG.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d833497c..3d78b3576 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ All notable changes to this project will be documented in this file. + +## [v1.40.0] - 2020-09-22 + +- feat: Add possibility to share tflint config file for subdirs ([#149](https://github.com/antonbabenko/pre-commit-terraform/issues/149)) + + ## [v1.39.0] - 2020-09-08 @@ -340,7 +346,8 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.39.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.40.0...HEAD +[v1.40.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.39.0...v1.40.0 [v1.39.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.38.0...v1.39.0 [v1.38.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.37.0...v1.38.0 [v1.37.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.36.0...v1.37.0 From 6f3b12514bb6c0c39dcdd095b873040411d31d79 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Wed, 23 Sep 2020 22:11:09 +0200 Subject: [PATCH 097/214] fix: terraform-docs version 0.10 removed with-aggregate-type-defaults (#150) --- .pre-commit-hooks.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 5763b110c..4b667db8f 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -11,14 +11,13 @@ description: Inserts input and output documentation into README.md (using terraform-docs). require_serial: true entry: terraform_docs.sh - args: [--args=--with-aggregate-type-defaults] language: script files: (\.tf)$ exclude: \.terraform\/.*$ - id: terraform_docs_without_aggregate_type_defaults name: Terraform docs (without aggregate type defaults) - description: Inserts input and output documentation into README.md (using terraform-docs). + description: Inserts input and output documentation into README.md (using terraform-docs). Identical to terraform_docs. require_serial: true entry: terraform_docs.sh language: script From cc4e5e85ba3674d36ea61d85e5d9a1144ae71cb9 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Wed, 23 Sep 2020 22:12:01 +0200 Subject: [PATCH 098/214] Updated CHANGELOG --- CHANGELOG.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d78b3576..6ed3431e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ All notable changes to this project will be documented in this file. + +## [v1.41.0] - 2020-09-23 + +- fix: terraform-docs version 0.10 removed with-aggregate-type-defaults ([#150](https://github.com/antonbabenko/pre-commit-terraform/issues/150)) + + ## [v1.40.0] - 2020-09-22 @@ -346,7 +352,8 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.40.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.41.0...HEAD +[v1.41.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.40.0...v1.41.0 [v1.40.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.39.0...v1.40.0 [v1.39.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.38.0...v1.39.0 [v1.38.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.37.0...v1.38.0 From 81770aa0fab607a0c2472f06c94afc7bff4fdc44 Mon Sep 17 00:00:00 2001 From: Matias Zilli Date: Thu, 24 Sep 2020 11:37:08 +0200 Subject: [PATCH 099/214] fix: make terraform_docs Windows compatible (#129) --- terraform_docs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform_docs.sh b/terraform_docs.sh index ae898bcd3..5931943dc 100755 --- a/terraform_docs.sh +++ b/terraform_docs.sh @@ -52,7 +52,7 @@ terraform_docs_() { local -a -r files=("$@") local hack_terraform_docs - hack_terraform_docs=$(terraform version | head -1 | grep -c 0.12) || true + hack_terraform_docs=$(terraform version | sed -n 1p | grep -c 0.12) || true 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." From 6b03062a175a01321946fdbb774e655938c97034 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Thu, 24 Sep 2020 13:35:05 +0200 Subject: [PATCH 100/214] Updated CHANGELOG --- CHANGELOG.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ed3431e2..6ca6a2c43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ All notable changes to this project will be documented in this file. + +## [v1.42.0] - 2020-09-24 + +- fix: make terraform_docs Windows compatible ([#129](https://github.com/antonbabenko/pre-commit-terraform/issues/129)) + + ## [v1.41.0] - 2020-09-23 @@ -352,7 +358,8 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.41.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.42.0...HEAD +[v1.42.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.41.0...v1.42.0 [v1.41.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.40.0...v1.41.0 [v1.40.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.39.0...v1.40.0 [v1.39.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.38.0...v1.39.0 From d773f4ad79e185710ca52fab5f66c0e6c3e6b12c Mon Sep 17 00:00:00 2001 From: Evan Stoddard Date: Thu, 24 Sep 2020 14:44:05 -0500 Subject: [PATCH 101/214] fix: Fix regex considering terraform-docs v0.10.0 old (#151) --- terraform_docs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform_docs.sh b/terraform_docs.sh index 5931943dc..ee1ff90e4 100755 --- a/terraform_docs.sh +++ b/terraform_docs.sh @@ -60,7 +60,7 @@ terraform_docs_() { fi local is_old_terraform_docs - is_old_terraform_docs=$(terraform-docs version | grep -o "v0.[1-7]" | tail -1) || true + 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) From 84374f64a05e53a23d8e23177db09ae964434f64 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Thu, 24 Sep 2020 21:44:33 +0200 Subject: [PATCH 102/214] Updated CHANGELOG --- CHANGELOG.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ca6a2c43..dee1893ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ All notable changes to this project will be documented in this file. + +## [v1.43.0] - 2020-09-24 + +- fix: Fix regex considering terraform-docs v0.10.0 old ([#151](https://github.com/antonbabenko/pre-commit-terraform/issues/151)) + + ## [v1.42.0] - 2020-09-24 @@ -358,7 +364,8 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.42.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.43.0...HEAD +[v1.43.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.42.0...v1.43.0 [v1.42.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.41.0...v1.42.0 [v1.41.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.40.0...v1.41.0 [v1.40.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.39.0...v1.40.0 From d303bff1f9a7fadc4b22db6a642861de6dfca2c9 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Mon, 2 Nov 2020 21:44:54 +0100 Subject: [PATCH 103/214] feat: Make terraform_validate to run init if necessary (#158) --- .pre-commit-config.yaml | 2 +- README.md | 10 +++++++- terraform_docs.sh | 2 +- terraform_tfsec.sh | 2 +- terraform_validate.sh | 51 ++++++++++++++++++++++------------------- 5 files changed, 40 insertions(+), 27 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 90e69ba8f..3fd97750b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: git://github.com/pre-commit/pre-commit-hooks - rev: v3.2.0 + rev: v3.3.0 hooks: - id: check-yaml - id: end-of-file-fixer diff --git a/README.md b/README.md index c54a5ec56..5647336f0 100644 --- a/README.md +++ b/README.md @@ -195,13 +195,21 @@ if they are present in `README.md`. - '--envs=AWS_SECRET_ACCESS_KEY="asecretkey"' ``` +1. It may happen that Terraform working directory (`.terraform`) already exists but not in the best condition (eg, not initialized modules, wrong version of Terraform, etc). To solve this problem you can find and delete all `.terraform` directories in your repository using this command: + + ```shell + find . -type d -name ".terraform" -print0 | xargs -0 rm -r + ``` + + `terraform_validate` hook will try to reinitialize them before running `terraform validate` command. + ## Notes for developers 1. Python hooks are supported now too. All you have to do is: 1. add a line to the `console_scripts` array in `entry_points` in `setup.py` 1. Put your python script in the `pre_commit_hooks` folder -Enjoy the clean and documented code! +Enjoy the clean, valid, and documented code! ## Authors diff --git a/terraform_docs.sh b/terraform_docs.sh index ee1ff90e4..8c7076bcf 100755 --- a/terraform_docs.sh +++ b/terraform_docs.sh @@ -311,7 +311,7 @@ EOF } -# global arrays +# global arrays declare -a ARGS=() declare -a FILES=() diff --git a/terraform_tfsec.sh b/terraform_tfsec.sh index a698420c6..6c6ee25a5 100755 --- a/terraform_tfsec.sh +++ b/terraform_tfsec.sh @@ -66,7 +66,7 @@ parse_cmdline_() { done } -# global arrays +# global arrays declare -a ARGS=() declare -a FILES=() diff --git a/terraform_validate.sh b/terraform_validate.sh index c224fa88a..236b35138 100755 --- a/terraform_validate.sh +++ b/terraform_validate.sh @@ -1,6 +1,9 @@ #!/usr/bin/env bash set -eo pipefail +# `terraform validate` requires this env variable to be set +export AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION:-us-east-1} + main() { initialize_ parse_cmdline_ "$@" @@ -80,34 +83,36 @@ terraform_validate_() { if [[ -n "$(find "$path_uniq" -maxdepth 1 -name '*.tf' -print -quit)" ]]; then - local starting_path - starting_path=$(realpath "$path_uniq") - local terraform_path - terraform_path="$path_uniq" - - # Find the relevant .terraform directory (indicating a 'terraform init'), - # but fall through to the current directory. - while [[ $terraform_path != "." ]]; do - if [[ -d $terraform_path/.terraform ]]; then - break - else - terraform_path=$(dirname "$terraform_path") + pushd "$(realpath "$path_uniq")" > /dev/null + + if [[ ! -d .terraform ]]; then + set +e + init_output=$(terraform init -backend=false 2>&1) + init_code=$? + set -e + + if [[ $init_code != 0 ]]; then + error=1 + echo "Init before validation failed: $path_uniq" + echo "$init_output" + popd > /dev/null + continue fi - done + fi - local validate_path - validate_path="${path_uniq#"$terraform_path"}" + set +e + validate_output=$(terraform validate "${ARGS[@]}" 2>&1) + validate_code=$? + set -e - # Change to the directory that has been initialized, run validation, then - # change back to the starting directory. - cd "$(realpath "$terraform_path")" - if ! terraform validate "${ARGS[@]}" "$validate_path"; then + if [[ $validate_code != 0 ]]; then error=1 + echo "Validation failed: $path_uniq" + echo "$validate_output" echo - echo "Failed path: $path_uniq" - echo "================================" fi - cd "$starting_path" + + popd > /dev/null fi done @@ -116,7 +121,7 @@ terraform_validate_() { fi } -# global arrays +# global arrays declare -a ARGS declare -a ENVS declare -a FILES From fe2d121d123cd3147d849dd0a825e534da976008 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Mon, 2 Nov 2020 21:48:24 +0100 Subject: [PATCH 104/214] Updated CHANGELOG --- CHANGELOG.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dee1893ab..6fb3d25c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ All notable changes to this project will be documented in this file. + +## [v1.44.0] - 2020-11-02 + +- feat: Make terraform_validate to run init if necessary ([#158](https://github.com/antonbabenko/pre-commit-terraform/issues/158)) + + ## [v1.43.0] - 2020-09-24 @@ -364,7 +370,8 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.43.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.44.0...HEAD +[v1.44.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.43.0...v1.44.0 [v1.43.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.42.0...v1.43.0 [v1.42.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.41.0...v1.42.0 [v1.41.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.40.0...v1.41.0 From 3a07570ddb9f8e5a6a3d3fd9164ef6ae8694883e Mon Sep 17 00:00:00 2001 From: Shawn Date: Thu, 12 Nov 2020 05:55:53 -0500 Subject: [PATCH 105/214] fix: Correct deprecated parameter to terraform-docs (#156) --- README.md | 6 +++--- pre_commit_hooks/terraform_docs_replace.py | 9 ++++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 5647336f0..03fcf00a9 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ There are several [pre-commit](https://pre-commit.com/) hooks to keep Terraform | `terraform_validate` | Validates all Terraform configuration files. | | `terraform_docs` | Inserts input and output documentation into `README.md`. Recommended. | | `terraform_docs_without_aggregate_type_defaults` | Inserts input and output documentation into `README.md` without aggregate type defaults. | -| `terraform_docs_replace` | Runs `terraform-docs` and pipes the output directly to README.md | +| `terraform_docs_replace` | Runs `terraform-docs` and pipes the output directly to README.md (requires terraform-docs v0.10.0 or later) | | `terraform_tflint` | Validates all Terraform configuration files with [TFLint](https://github.com/terraform-linters/tflint). | | `terragrunt_fmt` | Rewrites all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) to a canonical format. | | `terragrunt_validate` | Validates all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) | @@ -91,13 +91,13 @@ Check the [source file](https://github.com/antonbabenko/pre-commit-terraform/blo ``` if they are present in `README.md`. -1. `terraform_docs_replace` replaces the entire README.md rather than doing string replacement between markers. Put your additional documentation at the top of your `main.tf` for it to be pulled in. The optional `--dest` argument lets you change the name of the file that gets created/modified. +1. `terraform_docs_replace` replaces the entire README.md rather than doing string replacement between markers. Put your additional documentation at the top of your `main.tf` for it to be pulled in. The optional `--dest` argument lets you change the name of the file that gets created/modified. This hook requires terraform-docs v0.10.0 or later. 1. Example: ```yaml hooks: - id: terraform_docs_replace - args: ['--with-aggregate-type-defaults', '--sort-inputs-by-required', '--dest=TEST.md'] + args: ['--sort-by-required', '--dest=TEST.md'] ``` 1. It is possible to pass additional arguments to shell scripts when using `terraform_docs` and `terraform_docs_without_aggregate_type_defaults`. Send pull-request with the new hook if there is something missing. diff --git a/pre_commit_hooks/terraform_docs_replace.py b/pre_commit_hooks/terraform_docs_replace.py index 05fec622e..e1777b306 100644 --- a/pre_commit_hooks/terraform_docs_replace.py +++ b/pre_commit_hooks/terraform_docs_replace.py @@ -15,9 +15,14 @@ def main(argv=None): ) parser.add_argument( '--sort-inputs-by-required', dest='sort', action='store_true', + help='[deprecated] use --sort-by-required instead', + ) + parser.add_argument( + '--sort-by-required', dest='sort', action='store_true', ) parser.add_argument( '--with-aggregate-type-defaults', dest='aggregate', action='store_true', + help='[deprecated]', ) parser.add_argument('filenames', nargs='*', help='Filenames to check.') args = parser.parse_args(argv) @@ -35,9 +40,7 @@ def main(argv=None): procArgs = [] procArgs.append('terraform-docs') if args.sort: - procArgs.append('--sort-inputs-by-required') - if args.aggregate: - procArgs.append('--with-aggregate-type-defaults') + procArgs.append('--sort-by-required') procArgs.append('md') procArgs.append("./{dir}".format(dir=dir)) procArgs.append("| sed -e '$ d' -e 'N;/^\\n$/D;P;D'") From 4f6e84905971ddf36f486e912f25878b135debe5 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Thu, 12 Nov 2020 11:58:02 +0100 Subject: [PATCH 106/214] Updated CHANGELOG --- CHANGELOG.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fb3d25c3..5c86e9a58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,9 +7,20 @@ All notable changes to this project will be documented in this file. + +## [v1.45.0] - 2020-11-12 + +- fix: Correct deprecated parameter to terraform-docs ([#156](https://github.com/antonbabenko/pre-commit-terraform/issues/156)) + + ## [v1.44.0] - 2020-11-02 + + + +## [v1.43.1] - 2020-11-02 + - feat: Make terraform_validate to run init if necessary ([#158](https://github.com/antonbabenko/pre-commit-terraform/issues/158)) @@ -370,8 +381,10 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.44.0...HEAD -[v1.44.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.43.0...v1.44.0 +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.45.0...HEAD +[v1.45.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.44.0...v1.45.0 +[v1.44.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.43.1...v1.44.0 +[v1.43.1]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.43.0...v1.43.1 [v1.43.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.42.0...v1.43.0 [v1.42.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.41.0...v1.42.0 [v1.41.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.40.0...v1.41.0 From dc87b07c7c037e64fd8a3ae67b77943e2f72d44c Mon Sep 17 00:00:00 2001 From: Sharif Nassar Date: Tue, 24 Nov 2020 18:10:47 -0800 Subject: [PATCH 107/214] update to upstream hooks --- .pre-commit-hooks.yaml | 82 +++++++++++++++++++++++++++++++++--------- 1 file changed, 66 insertions(+), 16 deletions(-) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 2e15aab48..7b70839a5 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -5,38 +5,88 @@ language: script files: (\.tf|\.tfvars)$ exclude: \.terraform\/.*$ - require_serial: true - id: terraform_docs - name: Terraform Docs - description: Creates readme for terraform modules. + name: Terraform docs + description: Inserts input and output documentation into README.md (using terraform-docs). + require_serial: true entry: terraform_docs.sh language: script files: (\.tf)$ exclude: \.terraform\/.*$ -- id: terraform_readme - name: Terraform Readme - description: Creates a README for Terraform modules using `terraform_config_inspect`. - entry: terraform_readme.sh +- id: terraform_docs_without_aggregate_type_defaults + name: Terraform docs (without aggregate type defaults) + description: Inserts input and output documentation into README.md (using terraform-docs). Identical to terraform_docs. + require_serial: true + entry: terraform_docs.sh language: script files: (\.tf)$ exclude: \.terraform\/.*$ - require_serial: true +- id: terraform_docs_replace + name: Terraform docs (overwrite README.md) + description: Overwrite content of README.md with terraform-docs. + require_serial: true + entry: terraform_docs_replace + language: python + files: (\.tf)$ + exclude: \.terraform\/.*$ -- id: terraform_validate_no_variables - name: Terraform validate without variables - description: Validates all Terraform configuration files without checking whether all required variables were set (basic check). - entry: terraform_validate_no_variables.sh +- id: terraform_validate + name: Terraform validate + description: Validates all Terraform configuration files. + entry: terraform_validate.sh language: script files: (\.tf|\.tfvars)$ exclude: \.terraform\/.*$ -- id: terraform_validate_with_variables - name: Terraform validate with variables - description: Validates all Terraform configuration files and checks whether all required variables were specified. - entry: terraform_validate_with_variables.sh +- id: terraform_tflint + name: Terraform validate with tflint + description: Validates all Terraform configuration files with TFLint. + entry: terraform_tflint.sh language: script files: (\.tf|\.tfvars)$ exclude: \.terraform\/.*$ + +- id: terragrunt_fmt + name: Terragrunt fmt + description: Rewrites all Terragrunt configuration files to a canonical format. + entry: terragrunt_fmt.sh + language: script + files: (\.hcl)$ + exclude: \.terraform\/.*$ + +- id: terragrunt_validate + name: Terragrunt validate + description: Validates all Terragrunt configuration files. + entry: terragrunt_validate.sh + language: script + files: (\.hcl)$ + exclude: \.terraform\/.*$ + +- id: terraform_tfsec + name: Terraform validate with tfsec + description: Static analysis of Terraform templates to spot potential security issues. + entry: terraform_tfsec.sh + language: script + +- id: checkov + name: Checkov + description: Runs checkov on Terraform templates. + entry: checkov -d . + language: python + pass_filenames: false + always_run: false + files: \.tf$ + exclude: \.+.terraform\/.*$ + require_serial: true + +- id: terraform_readme + name: Terraform Readme + description: Creates a README for Terraform modules using `terraform_config_inspect`. + entry: terraform_readme.sh + language: script + files: (\.tf)$ + exclude: \.terraform\/.*$ + require_serial: true From 333b080eac1c51d4d021cd044ba76d0b3f40343b Mon Sep 17 00:00:00 2001 From: Sharif Nassar Date: Tue, 24 Nov 2020 18:32:49 -0800 Subject: [PATCH 108/214] just report, do not error --- .pre-commit-hooks.yaml | 5 +++ terraform_tfsec_test.sh | 73 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100755 terraform_tfsec_test.sh diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index e9355135f..820d7055a 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -91,3 +91,8 @@ exclude: \.terraform\/.*$ require_serial: true +- id: terraform_tfsec_report_only + name: Terraform validate with tfsec + description: Static analysis of Terraform templates to spot potential security issues. + entry: terraform_tfsec_test.sh + language: script diff --git a/terraform_tfsec_test.sh b/terraform_tfsec_test.sh new file mode 100755 index 000000000..8b16044f0 --- /dev/null +++ b/terraform_tfsec_test.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash +set -eo pipefail + +main() { + initialize_ + parse_cmdline_ "$@" + + # propagate $FILES to custom function + tfsec_ "$ARGS" "$FILES" +} + +tfsec_() { + # consume modified files passed from pre-commit so that + # tfsec runs against only those relevant directories + for file_with_path in $FILES; do + file_with_path="${file_with_path// /__REPLACED__SPACE__}" + paths[index]=$(dirname "$file_with_path") + + let "index+=1" + done + + for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do + path_uniq="${path_uniq//__REPLACED__SPACE__/ }" + pushd "$path_uniq" > /dev/null + tfsec $ARGS || true + popd > /dev/null + done +} + +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_() { + declare argv + argv=$(getopt -o a: --long args: -- "$@") || return + eval "set -- $argv" + + for argv; do + case $argv in + -a | --args) + shift + ARGS+=("$1") + shift + ;; + --) + shift + FILES+=("$@") + break + ;; + esac + done +} + +# global arrays +declare -a ARGS=() +declare -a FILES=() + +[[ ${BASH_SOURCE[0]} != "$0" ]] || main "$@" From 827af52cd2ab47a67e15ecfa978cf00a53d67065 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Sat, 20 Feb 2021 20:18:07 +0100 Subject: [PATCH 109/214] fix: Terraform validate for submodules (#172) --- .pre-commit-config.yaml | 4 ++-- .pre-commit-hooks.yaml | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3fd97750b..a0304e23e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: git://github.com/pre-commit/pre-commit-hooks - rev: v3.3.0 + rev: v3.4.0 hooks: - id: check-yaml - id: end-of-file-fixer @@ -9,7 +9,7 @@ repos: - id: check-merge-conflict - id: check-executables-have-shebangs - repo: git://github.com/jumanjihouse/pre-commit-hooks - rev: 2.1.4 + rev: 2.1.5 hooks: - id: shfmt args: ['-l', '-i', '2', '-ci', '-sr', '-w'] diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 4b667db8f..d7b2d61d6 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -36,6 +36,7 @@ - id: terraform_validate name: Terraform validate description: Validates all Terraform configuration files. + require_serial: true entry: terraform_validate.sh language: script files: (\.tf|\.tfvars)$ From f189a114b1771f473654606105f3edd9288369a7 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Sat, 20 Feb 2021 20:18:54 +0100 Subject: [PATCH 110/214] Updated CHANGELOG --- CHANGELOG.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c86e9a58..8170d93fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ All notable changes to this project will be documented in this file. + +## [v1.46.0] - 2021-02-20 + +- fix: Terraform validate for submodules ([#172](https://github.com/antonbabenko/pre-commit-terraform/issues/172)) + + ## [v1.45.0] - 2020-11-12 @@ -117,7 +123,7 @@ All notable changes to this project will be documented in this file. - fix: Change terraform_validate hook functionality for subdirectories with terraform files ([#100](https://github.com/antonbabenko/pre-commit-terraform/issues/100)) -### +### configuration for the appropriate working directory. @@ -381,7 +387,8 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.45.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.46.0...HEAD +[v1.46.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.45.0...v1.46.0 [v1.45.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.44.0...v1.45.0 [v1.44.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.43.1...v1.44.0 [v1.43.1]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.43.0...v1.43.1 From ad5dccae65a1508497dd638c7b511099296edaf3 Mon Sep 17 00:00:00 2001 From: Manuel Vogel Date: Thu, 25 Feb 2021 17:03:46 +0100 Subject: [PATCH 111/214] docs: updates installs for macOS and ubuntu (#175) --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 03fcf00a9..31851120d 100644 --- a/README.md +++ b/README.md @@ -16,18 +16,17 @@ ##### MacOS ```bash -brew tap liamg/tfsec brew install pre-commit gawk terraform-docs tflint tfsec coreutils ``` ##### Ubuntu ```bash -sudo apt install python3-pip gawk &&\ +sudo apt install python3-pip gawk unzip &&\ pip3 install pre-commit -curl -L "$(curl -s https://api.github.com/repos/terraform-docs/terraform-docs/releases/latest | grep -o -E "https://.+?-linux-amd64")" > terraform-docs && chmod +x terraform-docs && sudo mv terraform-docs /usr/bin/ +curl -L "$(curl -s https://api.github.com/repos/terraform-docs/terraform-docs/releases/latest | grep -o -E "https://.+?-linux-amd64.tar.gz")" > terraform-docs.tgz && tar xzf terraform-docs.tgz && chmod +x terraform-docs && sudo mv terraform-docs /usr/bin/ curl -L "$(curl -s https://api.github.com/repos/terraform-linters/tflint/releases/latest | grep -o -E "https://.+?_linux_amd64.zip")" > tflint.zip && unzip tflint.zip && rm tflint.zip && sudo mv tflint /usr/bin/ -env GO111MODULE=on go get -u github.com/liamg/tfsec/cmd/tfsec +env GO111MODULE=on go get -u github.com/tfsec/tfsec/cmd/tfsec ``` ### 2. Install the pre-commit hook globally From 90d45213a304bb5b2f90876f6f2621e1a87c92da Mon Sep 17 00:00:00 2001 From: chopped pork Date: Thu, 25 Feb 2021 19:46:51 +0000 Subject: [PATCH 112/214] fix: remove sed postprocessing from the terraform_docs_replace hook to fix compatibility with terraform-docs 0.11.0+ (#176) --- CHANGELOG.md | 3 ++- pre_commit_hooks/terraform_docs_replace.py | 5 ++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8170d93fa..aa8f27c4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +- fix: Removes sed post-processing from the `terraform_docs_replace` hook which was causing the last line to be missing when using `terraform-docs` 0.11.0+. Note: for older versions this change will result in an extra newline at the end of the file (making the pre-commit hook and `terraform-docs` output identical). @@ -123,7 +124,7 @@ All notable changes to this project will be documented in this file. - fix: Change terraform_validate hook functionality for subdirectories with terraform files ([#100](https://github.com/antonbabenko/pre-commit-terraform/issues/100)) -### +### configuration for the appropriate working directory. diff --git a/pre_commit_hooks/terraform_docs_replace.py b/pre_commit_hooks/terraform_docs_replace.py index e1777b306..a9cf6c9bc 100644 --- a/pre_commit_hooks/terraform_docs_replace.py +++ b/pre_commit_hooks/terraform_docs_replace.py @@ -29,7 +29,7 @@ def main(argv=None): dirs = [] for filename in args.filenames: - if (os.path.realpath(filename) not in dirs and \ + if (os.path.realpath(filename) not in dirs and (filename.endswith(".tf") or filename.endswith(".tfvars"))): dirs.append(os.path.dirname(filename)) @@ -43,9 +43,8 @@ def main(argv=None): procArgs.append('--sort-by-required') procArgs.append('md') procArgs.append("./{dir}".format(dir=dir)) - procArgs.append("| sed -e '$ d' -e 'N;/^\\n$/D;P;D'") procArgs.append('>') - procArgs.append("./{dir}/{dest}".format(dir=dir,dest=args.dest)) + procArgs.append("./{dir}/{dest}".format(dir=dir, dest=args.dest)) subprocess.check_call(" ".join(procArgs), shell=True) except subprocess.CalledProcessError as e: print(e) From 47b80ec9d58e38679bc6caae9253efa2d5325b0c Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Thu, 25 Feb 2021 20:47:19 +0100 Subject: [PATCH 113/214] Updated CHANGELOG --- CHANGELOG.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa8f27c4c..d5da4b95a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,13 @@ All notable changes to this project will be documented in this file. ## [Unreleased] -- fix: Removes sed post-processing from the `terraform_docs_replace` hook which was causing the last line to be missing when using `terraform-docs` 0.11.0+. Note: for older versions this change will result in an extra newline at the end of the file (making the pre-commit hook and `terraform-docs` output identical). + + + +## [v1.47.0] - 2021-02-25 + +- fix: remove sed postprocessing from the terraform_docs_replace hook to fix compatibility with terraform-docs 0.11.0+ ([#176](https://github.com/antonbabenko/pre-commit-terraform/issues/176)) +- docs: updates installs for macOS and ubuntu ([#175](https://github.com/antonbabenko/pre-commit-terraform/issues/175)) @@ -124,7 +130,7 @@ All notable changes to this project will be documented in this file. - fix: Change terraform_validate hook functionality for subdirectories with terraform files ([#100](https://github.com/antonbabenko/pre-commit-terraform/issues/100)) -### +### configuration for the appropriate working directory. @@ -388,7 +394,8 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.46.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.47.0...HEAD +[v1.47.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.46.0...v1.47.0 [v1.46.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.45.0...v1.46.0 [v1.45.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.44.0...v1.45.0 [v1.44.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.43.1...v1.44.0 From 53de83359eb2426668266094f5f73ef7651921f9 Mon Sep 17 00:00:00 2001 From: Manuel Vogel Date: Fri, 12 Mar 2021 10:32:41 +0100 Subject: [PATCH 114/214] docs: Added checkov install (#182) --- README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 31851120d..36f6cec89 100644 --- a/README.md +++ b/README.md @@ -16,17 +16,21 @@ ##### MacOS ```bash -brew install pre-commit gawk terraform-docs tflint tfsec coreutils +brew install pre-commit gawk terraform-docs tflint tfsec coreutils checkov ``` -##### Ubuntu +##### Ubuntu 18.04 ```bash -sudo apt install python3-pip gawk unzip &&\ +sudo apt update +sudo apt install -y gawk unzip software-properties-common +sudo add-apt-repository ppa:deadsnakes/ppa +sudo apt install -y python3.7 python3-pip pip3 install pre-commit curl -L "$(curl -s https://api.github.com/repos/terraform-docs/terraform-docs/releases/latest | grep -o -E "https://.+?-linux-amd64.tar.gz")" > terraform-docs.tgz && tar xzf terraform-docs.tgz && chmod +x terraform-docs && sudo mv terraform-docs /usr/bin/ curl -L "$(curl -s https://api.github.com/repos/terraform-linters/tflint/releases/latest | grep -o -E "https://.+?_linux_amd64.zip")" > tflint.zip && unzip tflint.zip && rm tflint.zip && sudo mv tflint /usr/bin/ env GO111MODULE=on go get -u github.com/tfsec/tfsec/cmd/tfsec +python3.7 -m pip install -U checkovpython3.7 -m pip install -U checkov ``` ### 2. Install the pre-commit hook globally From 36a269f1093a76a8ef6d603b35cb067380cac70f Mon Sep 17 00:00:00 2001 From: Manuel Vogel Date: Fri, 12 Mar 2021 15:35:21 +0100 Subject: [PATCH 115/214] chore: add dockerfile (#183) --- .dockerignore | 3 +++ Dockerfile | 47 +++++++++++++++++++++++++++++++++++++++++++++++ README.md | 12 ++++++++++++ 3 files changed, 62 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..763bb31a8 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +* +!.dockerignore +!Dockerfile \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..2488ec41e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,47 @@ +FROM ubuntu:18.04 + +ARG PRE_COMMIT_VERSION="2.11.1" +ARG GOLANG_VERSION="1.16" +ARG TERRAFORM_VERSION="0.14.8" +ARG TFSEC_VERSION="v0.39.6" +ARG TERRAFORM_DOCS_VERSION="latest" +ARG TFLINT_VERSION="latest" +ARG TFSEC_VERSION="v0.39.6" +ARG CHECKOV_VERSION="1.0.838" + +# Install general dependencies +RUN apt update && \ + apt install -y curl git gawk unzip software-properties-common + +# Install golang +RUN curl -L https://dl.google.com/go/go${GOLANG_VERSION}.linux-amd64.tar.gz > go${GOLANG_VERSION}.linux-amd64.tar.gz && \ + tar xzf go${GOLANG_VERSION}.linux-amd64.tar.gz && \ + rm -f go${GOLANG_VERSION}.linux-amd64.tar.gz +ENV GOPATH /go +RUN mkdir -p "$GOPATH/src" "$GOPATH/bin" && chmod -R 777 "$GOPATH" +ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH + +# Install tools +RUN add-apt-repository ppa:deadsnakes/ppa && \ + apt install -y python3.7 python3-pip && \ + pip3 install pre-commit==${PRE_COMMIT_VERSION} && \ + curl -L "$(curl -s https://api.github.com/repos/terraform-docs/terraform-docs/releases/${TERRAFORM_DOCS_VERSION} | grep -o -E "https://.+?-linux-amd64.tar.gz")" > terraform-docs.tgz && tar xzf terraform-docs.tgz && chmod +x terraform-docs && mv terraform-docs /usr/bin/ && \ + curl -L "$(curl -s https://api.github.com/repos/terraform-linters/tflint/releases/${TFLINT_VERSION} | grep -o -E "https://.+?_linux_amd64.zip")" > tflint.zip && unzip tflint.zip && rm tflint.zip && mv tflint /usr/bin/ && \ + python3.7 -m pip install -U checkov==${CHECKOV_VERSION} +RUN env GO111MODULE=on go get -u github.com/tfsec/tfsec/cmd/tfsec@${TFSEC_VERSION} + +# Install terraform because pre-commit needs it +RUN curl -fsSL https://apt.releases.hashicorp.com/gpg | apt-key add - && \ + apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main" && \ + apt-get update && apt-get install terraform=${TERRAFORM_VERSION} + +# Checking all binaries are in the PATH +RUN go version +RUN terraform --help +RUN pre-commit --help +RUN terraform-docs --help +RUN tflint --help +RUN tfsec --help +RUN checkov --help + +ENTRYPOINT [ "pre-commit" ] \ No newline at end of file diff --git a/README.md b/README.md index 36f6cec89..9edd0c21c 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ * [`coreutils`](https://formulae.brew.sh/formula/coreutils) required for `terraform_validate` hook on macOS (due to use of `realpath`). * [`checkov`](https://github.com/bridgecrewio/checkov) required for `checkov` hook. +or build and use the Docker image locally as mentioned below in the `Run` section. + ##### MacOS ```bash @@ -34,6 +36,7 @@ python3.7 -m pip install -U checkovpython3.7 -m pip install -U checkov ``` ### 2. Install the pre-commit hook globally +Note: not needed if you use the Docker image ```bash DIR=~/.git-template @@ -65,6 +68,15 @@ After pre-commit hook has been installed you can run it manually on all files in pre-commit run -a ``` +or you can also build and use the provided Docker container, which wraps all dependencies by +```bash +# first building it +docker build -t pre-commit . +# and then running it in the folder +# with the terraform code you want to check by executing +docker run -v $(pwd):/lint -w /lint pre-commit run -a +``` + ## Available Hooks There are several [pre-commit](https://pre-commit.com/) hooks to keep Terraform configurations (both `*.tf` and `*.tfvars`) and Terragrunt configurations (`*.hcl`) in a good shape: From 257824c637d2a4ef7843c792eb088dc8bc2c6140 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Fri, 12 Mar 2021 15:36:00 +0100 Subject: [PATCH 116/214] Updated CHANGELOG --- CHANGELOG.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d5da4b95a..b3835136c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ All notable changes to this project will be documented in this file. + +## [v1.48.0] - 2021-03-12 + +- chore: add dockerfile ([#183](https://github.com/antonbabenko/pre-commit-terraform/issues/183)) +- docs: Added checkov install ([#182](https://github.com/antonbabenko/pre-commit-terraform/issues/182)) + + ## [v1.47.0] - 2021-02-25 @@ -394,7 +401,8 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.47.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.48.0...HEAD +[v1.48.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.47.0...v1.48.0 [v1.47.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.46.0...v1.47.0 [v1.46.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.45.0...v1.46.0 [v1.45.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.44.0...v1.45.0 From bec7b5d943953f0c19afb22cbe2eae4689d937f3 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Thu, 18 Mar 2021 09:13:10 +0100 Subject: [PATCH 117/214] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9edd0c21c..68ee995e7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Collection of git hooks for Terraform to be used with [pre-commit framework](http://pre-commit.com/) -[![Github tag](https://img.shields.io/github/tag/antonbabenko/pre-commit-terraform.svg)](https://github.com/antonbabenko/pre-commit-terraform/releases) ![](https://img.shields.io/maintenance/yes/2020.svg) [![Help Contribute to Open Source](https://www.codetriage.com/antonbabenko/pre-commit-terraform/badges/users.svg)](https://www.codetriage.com/antonbabenko/pre-commit-terraform) +[![Github tag](https://img.shields.io/github/tag/antonbabenko/pre-commit-terraform.svg)](https://github.com/antonbabenko/pre-commit-terraform/releases) ![](https://img.shields.io/maintenance/yes/2021.svg) [![Help Contribute to Open Source](https://www.codetriage.com/antonbabenko/pre-commit-terraform/badges/users.svg)](https://www.codetriage.com/antonbabenko/pre-commit-terraform) ## How to install From fa3859e55f31a921152c0047f67edb62b564ebbc Mon Sep 17 00:00:00 2001 From: Sergio Kef Date: Wed, 24 Mar 2021 21:12:24 +0100 Subject: [PATCH 118/214] chore: Fix mistake on command (#185) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 68ee995e7..cd692e794 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ pip3 install pre-commit curl -L "$(curl -s https://api.github.com/repos/terraform-docs/terraform-docs/releases/latest | grep -o -E "https://.+?-linux-amd64.tar.gz")" > terraform-docs.tgz && tar xzf terraform-docs.tgz && chmod +x terraform-docs && sudo mv terraform-docs /usr/bin/ curl -L "$(curl -s https://api.github.com/repos/terraform-linters/tflint/releases/latest | grep -o -E "https://.+?_linux_amd64.zip")" > tflint.zip && unzip tflint.zip && rm tflint.zip && sudo mv tflint /usr/bin/ env GO111MODULE=on go get -u github.com/tfsec/tfsec/cmd/tfsec -python3.7 -m pip install -U checkovpython3.7 -m pip install -U checkov +python3.7 -m pip install -U checkov ``` ### 2. Install the pre-commit hook globally From d27074b5a03fb4ccfb9261c9999411af2358a742 Mon Sep 17 00:00:00 2001 From: Manuel Vogel Date: Tue, 20 Apr 2021 12:13:25 +0200 Subject: [PATCH 119/214] fix: Fix and pin versions in Dockerfile (#193) --- Dockerfile | 25 +++++++------------------ README.md | 2 +- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2488ec41e..1bc60ebbb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,34 +1,24 @@ FROM ubuntu:18.04 ARG PRE_COMMIT_VERSION="2.11.1" -ARG GOLANG_VERSION="1.16" -ARG TERRAFORM_VERSION="0.14.8" -ARG TFSEC_VERSION="v0.39.6" -ARG TERRAFORM_DOCS_VERSION="latest" -ARG TFLINT_VERSION="latest" -ARG TFSEC_VERSION="v0.39.6" +ARG TERRAFORM_VERSION="0.15.0" +ARG TFSEC_VERSION="v0.39.21" +ARG TERRAFORM_DOCS_VERSION="v0.12.0" +ARG TFLINT_VERSION="v0.27.0" ARG CHECKOV_VERSION="1.0.838" # Install general dependencies RUN apt update && \ apt install -y curl git gawk unzip software-properties-common -# Install golang -RUN curl -L https://dl.google.com/go/go${GOLANG_VERSION}.linux-amd64.tar.gz > go${GOLANG_VERSION}.linux-amd64.tar.gz && \ - tar xzf go${GOLANG_VERSION}.linux-amd64.tar.gz && \ - rm -f go${GOLANG_VERSION}.linux-amd64.tar.gz -ENV GOPATH /go -RUN mkdir -p "$GOPATH/src" "$GOPATH/bin" && chmod -R 777 "$GOPATH" -ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH - # Install tools RUN add-apt-repository ppa:deadsnakes/ppa && \ apt install -y python3.7 python3-pip && \ pip3 install pre-commit==${PRE_COMMIT_VERSION} && \ - curl -L "$(curl -s https://api.github.com/repos/terraform-docs/terraform-docs/releases/${TERRAFORM_DOCS_VERSION} | grep -o -E "https://.+?-linux-amd64.tar.gz")" > terraform-docs.tgz && tar xzf terraform-docs.tgz && chmod +x terraform-docs && mv terraform-docs /usr/bin/ && \ - curl -L "$(curl -s https://api.github.com/repos/terraform-linters/tflint/releases/${TFLINT_VERSION} | grep -o -E "https://.+?_linux_amd64.zip")" > tflint.zip && unzip tflint.zip && rm tflint.zip && mv tflint /usr/bin/ && \ + curl -L "$(curl -s https://api.github.com/repos/terraform-docs/terraform-docs/releases | grep -o -E "https://.+?${TERRAFORM_DOCS_VERSION}-linux-amd64.tar.gz")" > terraform-docs.tgz && tar xzf terraform-docs.tgz && chmod +x terraform-docs && mv terraform-docs /usr/bin/ && \ + curl -L "$(curl -s https://api.github.com/repos/terraform-linters/tflint/releases | grep -o -E "https://.+?/${TFLINT_VERSION}/tflint_linux_amd64.zip")" > tflint.zip && unzip tflint.zip && rm tflint.zip && mv tflint /usr/bin/ && \ + curl -L "$(curl -s https://api.github.com/repos/tfsec/tfsec/releases | grep -o -E "https://.+?/${TFSEC_VERSION}/tfsec-linux-amd64")" > tfsec && chmod +x tfsec && mv tfsec /usr/bin/ && \ python3.7 -m pip install -U checkov==${CHECKOV_VERSION} -RUN env GO111MODULE=on go get -u github.com/tfsec/tfsec/cmd/tfsec@${TFSEC_VERSION} # Install terraform because pre-commit needs it RUN curl -fsSL https://apt.releases.hashicorp.com/gpg | apt-key add - && \ @@ -36,7 +26,6 @@ RUN curl -fsSL https://apt.releases.hashicorp.com/gpg | apt-key add - && \ apt-get update && apt-get install terraform=${TERRAFORM_VERSION} # Checking all binaries are in the PATH -RUN go version RUN terraform --help RUN pre-commit --help RUN terraform-docs --help diff --git a/README.md b/README.md index cd692e794..a939111b3 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ sudo apt install -y python3.7 python3-pip pip3 install pre-commit curl -L "$(curl -s https://api.github.com/repos/terraform-docs/terraform-docs/releases/latest | grep -o -E "https://.+?-linux-amd64.tar.gz")" > terraform-docs.tgz && tar xzf terraform-docs.tgz && chmod +x terraform-docs && sudo mv terraform-docs /usr/bin/ curl -L "$(curl -s https://api.github.com/repos/terraform-linters/tflint/releases/latest | grep -o -E "https://.+?_linux_amd64.zip")" > tflint.zip && unzip tflint.zip && rm tflint.zip && sudo mv tflint /usr/bin/ -env GO111MODULE=on go get -u github.com/tfsec/tfsec/cmd/tfsec +curl -L "$(curl -s https://api.github.com/repos/tfsec/tfsec/releases/latest | grep -o -E "https://.+?tfsec-linux-amd64")" > tfsec && chmod +x tfsec && mv tfsec /usr/bin/ python3.7 -m pip install -U checkov ``` From 96346e74d90467918729f8acafccd56c47e1be68 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Tue, 20 Apr 2021 12:13:58 +0200 Subject: [PATCH 120/214] Updated CHANGELOG --- CHANGELOG.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3835136c..5686d7243 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ All notable changes to this project will be documented in this file. + +## [v1.49.0] - 2021-04-20 + +- fix: Fix and pin versions in Dockerfile ([#193](https://github.com/antonbabenko/pre-commit-terraform/issues/193)) +- chore: Fix mistake on command ([#185](https://github.com/antonbabenko/pre-commit-terraform/issues/185)) +- Update README.md + + ## [v1.48.0] - 2021-03-12 @@ -290,19 +298,23 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 ## [v1.9.0] - 2019-02-18 +- Added CHANGELOG.md - Added chglog (hi [@robinbowes](https://github.com/robinbowes) :)) +- Merge pull request [#33](https://github.com/antonbabenko/pre-commit-terraform/issues/33) from chrisgilmerproj/run_terraform_docs_in_serial - Require terraform-docs runs in serial to avoid pre-commit doing parallel operations on similar file paths ## [v1.8.1] - 2018-12-15 +- Merge pull request [#30](https://github.com/antonbabenko/pre-commit-terraform/issues/30) from RothAndrew/feature/fix_issue_29 - Fix bug not letting terraform_docs_replace work in the root directory of a repo ## [v1.8.0] - 2018-12-14 +- Merge pull request [#27](https://github.com/antonbabenko/pre-commit-terraform/issues/27) from RothAndrew/feature/new_hook - fix typo - Address requested changes - Add `--dest` argument @@ -315,6 +327,7 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 - Merge remote-tracking branch 'origin/master' into pr25 - Added followup after [#25](https://github.com/antonbabenko/pre-commit-terraform/issues/25) +- Merge pull request [#25](https://github.com/antonbabenko/pre-commit-terraform/issues/25) from getcloudnative/feat-pass-terraform-docs-opts - Add feature to pass options to terraform-docs. - Added license file (fixed [#21](https://github.com/antonbabenko/pre-commit-terraform/issues/21)) @@ -375,6 +388,8 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 - Added badges - Added formatting for tfvars (fixes [#4](https://github.com/antonbabenko/pre-commit-terraform/issues/4)) ([#6](https://github.com/antonbabenko/pre-commit-terraform/issues/6)) +- Merge pull request [#5](https://github.com/antonbabenko/pre-commit-terraform/issues/5) from schneems/schneems/codetriage-badge +- [ci skip] Get more Open Source Helpers @@ -383,6 +398,7 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 - Renamed shell script file to the correct one - Updated .pre-commit-hooks.yaml - Updated sha in README +- Merge pull request [#3](https://github.com/antonbabenko/pre-commit-terraform/issues/3) from pecigonzalo/master - Exclude .terraform even on subfolders @@ -401,7 +417,8 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.48.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.49.0...HEAD +[v1.49.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.48.0...v1.49.0 [v1.48.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.47.0...v1.48.0 [v1.47.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.46.0...v1.47.0 [v1.46.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.45.0...v1.46.0 From fee2387b6ce595773cd3437ca2cc4081061f5fbb Mon Sep 17 00:00:00 2001 From: Cesar Rodriguez Date: Thu, 22 Apr 2021 16:15:00 -0400 Subject: [PATCH 121/214] feat: Adds support for Terrascan (#195) --- .pre-commit-hooks.yaml | 6 ++++ README.md | 9 ++++-- terrascan.sh | 73 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 3 deletions(-) create mode 100755 terrascan.sh diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index d7b2d61d6..8796c4b9e 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -82,3 +82,9 @@ files: \.tf$ exclude: \.+.terraform\/.*$ require_serial: true + +- id: terrascan + name: terrascan + description: Runs terrascan on Terraform templates. + language: script + entry: terrascan.sh diff --git a/README.md b/README.md index a939111b3..462e25fdb 100644 --- a/README.md +++ b/README.md @@ -12,13 +12,14 @@ * [`TFSec`](https://github.com/liamg/tfsec) required for `terraform_tfsec` hook. * [`coreutils`](https://formulae.brew.sh/formula/coreutils) required for `terraform_validate` hook on macOS (due to use of `realpath`). * [`checkov`](https://github.com/bridgecrewio/checkov) required for `checkov` hook. +* [`terrascan`](https://github.com/accurics/terrascan) required for `terrascan` hook. or build and use the Docker image locally as mentioned below in the `Run` section. ##### MacOS ```bash -brew install pre-commit gawk terraform-docs tflint tfsec coreutils checkov +brew install pre-commit gawk terraform-docs tflint tfsec coreutils checkov terrascan ``` ##### Ubuntu 18.04 @@ -32,6 +33,7 @@ pip3 install pre-commit curl -L "$(curl -s https://api.github.com/repos/terraform-docs/terraform-docs/releases/latest | grep -o -E "https://.+?-linux-amd64.tar.gz")" > terraform-docs.tgz && tar xzf terraform-docs.tgz && chmod +x terraform-docs && sudo mv terraform-docs /usr/bin/ curl -L "$(curl -s https://api.github.com/repos/terraform-linters/tflint/releases/latest | grep -o -E "https://.+?_linux_amd64.zip")" > tflint.zip && unzip tflint.zip && rm tflint.zip && sudo mv tflint /usr/bin/ curl -L "$(curl -s https://api.github.com/repos/tfsec/tfsec/releases/latest | grep -o -E "https://.+?tfsec-linux-amd64")" > tfsec && chmod +x tfsec && mv tfsec /usr/bin/ +curl -L "$(curl -s https://api.github.com/repos/accurics/terrascan/releases/latest | grep -o -E "https://.+?_Linux_x86_64.tar.gz")" > terrascan.tar.gz && tar -xf terrascan.tar.gz terrascan && rm terrascan.tar.gz && sudo mv terrascan /usr/bin/ python3.7 -m pip install -U checkov ``` @@ -72,9 +74,9 @@ or you can also build and use the provided Docker container, which wraps all dep ```bash # first building it docker build -t pre-commit . -# and then running it in the folder +# and then running it in the folder # with the terraform code you want to check by executing -docker run -v $(pwd):/lint -w /lint pre-commit run -a +docker run -v $(pwd):/lint -w /lint pre-commit run -a ``` ## Available Hooks @@ -93,6 +95,7 @@ There are several [pre-commit](https://pre-commit.com/) hooks to keep Terraform | `terragrunt_validate` | Validates all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) | | `terraform_tfsec` | [TFSec](https://github.com/liamg/tfsec) static analysis of terraform templates to spot potential security issues. | | `checkov` | [checkov](https://github.com/bridgecrewio/checkov) static analysis of terraform templates to spot potential security issues. | +| `terrascan` | [terrascan](https://github.com/accurics/terrascan) Detect compliance and security violations. | Check the [source file](https://github.com/antonbabenko/pre-commit-terraform/blob/master/.pre-commit-hooks.yaml) to know arguments used for each hook. diff --git a/terrascan.sh b/terrascan.sh new file mode 100755 index 000000000..d8233068b --- /dev/null +++ b/terrascan.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash +set -eo pipefail + +main() { + initialize_ + parse_cmdline_ "$@" + + # propagate $FILES to custom function + terrascan_ "$ARGS" "$FILES" +} + +terrascan_() { + # consume modified files passed from pre-commit so that + # terrascan runs against only those relevant directories + for file_with_path in $FILES; do + file_with_path="${file_with_path// /__REPLACED__SPACE__}" + paths[index]=$(dirname "$file_with_path") + + let "index+=1" + done + + for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do + path_uniq="${path_uniq//__REPLACED__SPACE__/ }" + pushd "$path_uniq" > /dev/null + terrascan scan -i terraform $ARGS + popd > /dev/null + done +} + +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_() { + declare argv + argv=$(getopt -o a: --long args: -- "$@") || return + eval "set -- $argv" + + for argv; do + case $argv in + -a | --args) + shift + ARGS+=("$1") + shift + ;; + --) + shift + FILES+=("$@") + break + ;; + esac + done +} + +# global arrays +declare -a ARGS=() +declare -a FILES=() + +[[ ${BASH_SOURCE[0]} != "$0" ]] || main "$@" From 9b84f70efef7419e53c9526dff2e4a7d6bc9c78d Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Thu, 22 Apr 2021 22:16:26 +0200 Subject: [PATCH 122/214] Updated CHANGELOG --- CHANGELOG.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5686d7243..33739006a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ All notable changes to this project will be documented in this file. + +## [v1.50.0] - 2021-04-22 + +- feat: Adds support for Terrascan ([#195](https://github.com/antonbabenko/pre-commit-terraform/issues/195)) + + ## [v1.49.0] - 2021-04-20 @@ -417,7 +423,8 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.49.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.50.0...HEAD +[v1.50.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.49.0...v1.50.0 [v1.49.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.48.0...v1.49.0 [v1.48.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.47.0...v1.48.0 [v1.47.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.46.0...v1.47.0 From c7d6d002ed510c1a3e63af6d0e132e8abf2448d4 Mon Sep 17 00:00:00 2001 From: Lorenz Vanthillo Date: Tue, 4 May 2021 16:41:28 +0200 Subject: [PATCH 123/214] chore: Update Ubuntu install method (#198) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 462e25fdb..6e43ade5a 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ sudo apt install -y python3.7 python3-pip pip3 install pre-commit curl -L "$(curl -s https://api.github.com/repos/terraform-docs/terraform-docs/releases/latest | grep -o -E "https://.+?-linux-amd64.tar.gz")" > terraform-docs.tgz && tar xzf terraform-docs.tgz && chmod +x terraform-docs && sudo mv terraform-docs /usr/bin/ curl -L "$(curl -s https://api.github.com/repos/terraform-linters/tflint/releases/latest | grep -o -E "https://.+?_linux_amd64.zip")" > tflint.zip && unzip tflint.zip && rm tflint.zip && sudo mv tflint /usr/bin/ -curl -L "$(curl -s https://api.github.com/repos/tfsec/tfsec/releases/latest | grep -o -E "https://.+?tfsec-linux-amd64")" > tfsec && chmod +x tfsec && mv tfsec /usr/bin/ +curl -L "$(curl -s https://api.github.com/repos/tfsec/tfsec/releases/latest | grep -o -E "https://.+?tfsec-linux-amd64")" > tfsec && chmod +x tfsec && sudo mv tfsec /usr/bin/ curl -L "$(curl -s https://api.github.com/repos/accurics/terrascan/releases/latest | grep -o -E "https://.+?_Linux_x86_64.tar.gz")" > terrascan.tar.gz && tar -xf terrascan.tar.gz terrascan && rm terrascan.tar.gz && sudo mv terrascan /usr/bin/ python3.7 -m pip install -U checkov ``` From 5daffe87271a154e9199c5113540e5ef6438a65a Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Thu, 9 Sep 2021 12:38:43 +0300 Subject: [PATCH 124/214] docs: Initial docs improvement (#218) --- .dockerignore | 2 +- .editorconfig | 14 ++++ .pre-commit-config.yaml | 24 +++++- CHANGELOG.md | 2 +- Dockerfile | 4 +- README.md | 160 ++++++++++++++++++++++++++++------------ 6 files changed, 153 insertions(+), 53 deletions(-) create mode 100644 .editorconfig diff --git a/.dockerignore b/.dockerignore index 763bb31a8..50c8ea340 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,3 @@ * !.dockerignore -!Dockerfile \ No newline at end of file +!Dockerfile diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..74f9834a3 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[{*.{sh,py,md},Dockerfile}] +indent_size = 4 + +[*.md] +trim_trailing_whitespace = false diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a0304e23e..354e538dc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,13 +1,31 @@ repos: - repo: git://github.com/pre-commit/pre-commit-hooks - rev: v3.4.0 + rev: v4.0.1 hooks: - - id: check-yaml + # Git style + - id: check-added-large-files + - id: check-merge-conflict + - id: check-vcs-permalinks + - id: forbid-new-submodules + - id: no-commit-to-branch + + # Common errors - id: end-of-file-fixer - id: trailing-whitespace - - id: check-case-conflict + args: [--markdown-linebreak-ext=md] + - id: check-yaml - id: check-merge-conflict - id: check-executables-have-shebangs + + # Cross platform + - id: check-case-conflict + + # Security + - id: detect-aws-credentials + args: ['--allow-missing-credentials'] + - id: detect-private-key + + - repo: git://github.com/jumanjihouse/pre-commit-hooks rev: 2.1.5 hooks: diff --git a/CHANGELOG.md b/CHANGELOG.md index 33739006a..722de3489 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -151,7 +151,7 @@ All notable changes to this project will be documented in this file. - fix: Change terraform_validate hook functionality for subdirectories with terraform files ([#100](https://github.com/antonbabenko/pre-commit-terraform/issues/100)) -### +### configuration for the appropriate working directory. diff --git a/Dockerfile b/Dockerfile index 1bc60ebbb..5d3f8906b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ FROM ubuntu:18.04 ARG PRE_COMMIT_VERSION="2.11.1" ARG TERRAFORM_VERSION="0.15.0" -ARG TFSEC_VERSION="v0.39.21" +ARG TFSEC_VERSION="v0.39.21" ARG TERRAFORM_DOCS_VERSION="v0.12.0" ARG TFLINT_VERSION="v0.27.0" ARG CHECKOV_VERSION="1.0.838" @@ -33,4 +33,4 @@ RUN tflint --help RUN tfsec --help RUN checkov --help -ENTRYPOINT [ "pre-commit" ] \ No newline at end of file +ENTRYPOINT [ "pre-commit" ] diff --git a/README.md b/README.md index 6e43ade5a..8852df7d1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,25 @@ # Collection of git hooks for Terraform to be used with [pre-commit framework](http://pre-commit.com/) -[![Github tag](https://img.shields.io/github/tag/antonbabenko/pre-commit-terraform.svg)](https://github.com/antonbabenko/pre-commit-terraform/releases) ![](https://img.shields.io/maintenance/yes/2021.svg) [![Help Contribute to Open Source](https://www.codetriage.com/antonbabenko/pre-commit-terraform/badges/users.svg)](https://www.codetriage.com/antonbabenko/pre-commit-terraform) +[![Github tag](https://img.shields.io/github/tag/antonbabenko/pre-commit-terraform.svg)](https://github.com/antonbabenko/pre-commit-terraform/releases) ![maintenance status](https://img.shields.io/maintenance/yes/2021.svg) [![Help Contribute to Open Source](https://www.codetriage.com/antonbabenko/pre-commit-terraform/badges/users.svg)](https://www.codetriage.com/antonbabenko/pre-commit-terraform) + +* [How to install](#how-to-install) + * [1. Install dependencies](#1-install-dependencies) + * [MacOS](#macos) + * [Ubuntu 18.04](#ubuntu-1804) + * [Ubuntu 20.04](#ubuntu-2004) + * [2. Install the pre-commit hook globally](#2-install-the-pre-commit-hook-globally) + * [3. Add configs and hooks](#3-add-configs-and-hooks) + * [4. Run](#4-run) +* [Available Hooks](#available-hooks) +* [Hooks notes](#hooks-notes) + * [terraform_docs](#terraform_docs) + * [terraform_tflint](#terraform_tflint) + * [terraform_tfsec](#terraform_tfsec) + * [terraform_validate](#terraform_validate) +* [Notes for contributors](#notes-for-contributors) + * [Run and debug hooks locally](#run-and-debug-hooks-locally) +* [Authors](#authors) +* [License](#license) ## How to install @@ -16,28 +35,46 @@ or build and use the Docker image locally as mentioned below in the `Run` section. -##### MacOS +#### MacOS ```bash brew install pre-commit gawk terraform-docs tflint tfsec coreutils checkov terrascan ``` -##### Ubuntu 18.04 +#### Ubuntu 18.04 ```bash sudo apt update sudo apt install -y gawk unzip software-properties-common sudo add-apt-repository ppa:deadsnakes/ppa sudo apt install -y python3.7 python3-pip +python3 -m pip install --upgrade pip pip3 install pre-commit curl -L "$(curl -s https://api.github.com/repos/terraform-docs/terraform-docs/releases/latest | grep -o -E "https://.+?-linux-amd64.tar.gz")" > terraform-docs.tgz && tar xzf terraform-docs.tgz && chmod +x terraform-docs && sudo mv terraform-docs /usr/bin/ curl -L "$(curl -s https://api.github.com/repos/terraform-linters/tflint/releases/latest | grep -o -E "https://.+?_linux_amd64.zip")" > tflint.zip && unzip tflint.zip && rm tflint.zip && sudo mv tflint /usr/bin/ -curl -L "$(curl -s https://api.github.com/repos/tfsec/tfsec/releases/latest | grep -o -E "https://.+?tfsec-linux-amd64")" > tfsec && chmod +x tfsec && sudo mv tfsec /usr/bin/ +curl -L "$(curl -s https://api.github.com/repos/aquasecurity/tfsec/releases/latest | grep -o -E "https://.+?tfsec-linux-amd64" | head -n 1)" > tfsec && chmod +x tfsec && sudo mv tfsec /usr/bin/ curl -L "$(curl -s https://api.github.com/repos/accurics/terrascan/releases/latest | grep -o -E "https://.+?_Linux_x86_64.tar.gz")" > terrascan.tar.gz && tar -xf terrascan.tar.gz terrascan && rm terrascan.tar.gz && sudo mv terrascan /usr/bin/ python3.7 -m pip install -U checkov ``` +##### Ubuntu 20.04 + +```bash +sudo apt update +sudo apt install -y gawk unzip software-properties-common +sudo apt install -y python3 python3-pip +python3 -m pip install --upgrade pip +pip3 install pre-commit +curl -L "$(curl -s https://api.github.com/repos/terraform-docs/terraform-docs/releases/latest | grep -o -E "https://.+?-linux-amd64.tar.gz")" > terraform-docs.tgz && tar -xzf terraform-docs.tgz terraform-docs && chmod +x terraform-docs && sudo mv terraform-docs /usr/bin/ +curl -L "$(curl -s https://api.github.com/repos/terraform-linters/tflint/releases/latest | grep -o -E "https://.+?_linux_amd64.zip")" > tflint.zip && unzip tflint.zip && rm tflint.zip && sudo mv tflint /usr/bin/ +curl -L "$(curl -s https://api.github.com/repos/aquasecurity/tfsec/releases/latest | grep -o -E "https://.+?tfsec-linux-amd64" | head -n 1)" > tfsec && chmod +x tfsec && sudo mv tfsec /usr/bin/ +curl -L "$(curl -s https://api.github.com/repos/accurics/terrascan/releases/latest | grep -o -E "https://.+?_Linux_x86_64.tar.gz")" > terrascan.tar.gz && tar -xf terrascan.tar.gz terrascan && rm terrascan.tar.gz && sudo mv terrascan /usr/bin/ +pip3 install -U checkov +``` + + ### 2. Install the pre-commit hook globally + Note: not needed if you use the Docker image ```bash @@ -71,6 +108,7 @@ pre-commit run -a ``` or you can also build and use the provided Docker container, which wraps all dependencies by + ```bash # first building it docker build -t pre-commit . @@ -83,48 +121,54 @@ docker run -v $(pwd):/lint -w /lint pre-commit run -a There are several [pre-commit](https://pre-commit.com/) hooks to keep Terraform configurations (both `*.tf` and `*.tfvars`) and Terragrunt configurations (`*.hcl`) in a good shape: -| Hook name | Description | -| ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- | -| `terraform_fmt` | Rewrites all Terraform configuration files to a canonical format. | -| `terraform_validate` | Validates all Terraform configuration files. | -| `terraform_docs` | Inserts input and output documentation into `README.md`. Recommended. | -| `terraform_docs_without_aggregate_type_defaults` | Inserts input and output documentation into `README.md` without aggregate type defaults. | -| `terraform_docs_replace` | Runs `terraform-docs` and pipes the output directly to README.md (requires terraform-docs v0.10.0 or later) | -| `terraform_tflint` | Validates all Terraform configuration files with [TFLint](https://github.com/terraform-linters/tflint). | -| `terragrunt_fmt` | Rewrites all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) to a canonical format. | -| `terragrunt_validate` | Validates all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) | -| `terraform_tfsec` | [TFSec](https://github.com/liamg/tfsec) static analysis of terraform templates to spot potential security issues. | -| `checkov` | [checkov](https://github.com/bridgecrewio/checkov) static analysis of terraform templates to spot potential security issues. | -| `terrascan` | [terrascan](https://github.com/accurics/terrascan) Detect compliance and security violations. | +| Hook name | Description | +| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ | +| `terraform_fmt` | Rewrites all Terraform configuration files to a canonical format. [Hook notes](#terraform_docs) | +| `terraform_validate` | Validates all Terraform configuration files. [Hook notes](#terraform_validate) | +| `terraform_docs` | Inserts input and output documentation into `README.md`. Recommended. | +| `terraform_docs_without_aggregate_type_defaults` | Inserts input and output documentation into `README.md` without aggregate type defaults. | +| `terraform_docs_replace` | Runs `terraform-docs` and pipes the output directly to README.md | +| `terraform_tflint` | Validates all Terraform configuration files with [TFLint](https://github.com/terraform-linters/tflint). [Hook notes](#terraform_tflint). | +| `terragrunt_fmt` | Rewrites all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) to a canonical format. | +| `terragrunt_validate` | Validates all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) | +| `terraform_tfsec` | [TFSec](https://github.com/liamg/tfsec) static analysis of terraform templates to spot potential security issues. [Hook notes](#terraform_tfsec) | +| `checkov` | [checkov](https://github.com/bridgecrewio/checkov) static analysis of terraform templates to spot potential security issues. | +| `terrascan` | [terrascan](https://github.com/accurics/terrascan) Detect compliance and security violations. | Check the [source file](https://github.com/antonbabenko/pre-commit-terraform/blob/master/.pre-commit-hooks.yaml) to know arguments used for each hook. -## Notes about terraform_docs hooks +## Hooks notes + +### terraform_docs 1. `terraform_docs` and `terraform_docs_without_aggregate_type_defaults` will insert/update documentation generated by [terraform-docs](https://github.com/terraform-docs/terraform-docs) framed by markers: -```txt - - -``` -if they are present in `README.md`. + ```txt + + + + ``` + + if they are present in `README.md`. -1. `terraform_docs_replace` replaces the entire README.md rather than doing string replacement between markers. Put your additional documentation at the top of your `main.tf` for it to be pulled in. The optional `--dest` argument lets you change the name of the file that gets created/modified. This hook requires terraform-docs v0.10.0 or later. +2. `terraform_docs_replace` replaces the entire README.md rather than doing string replacement between markers. Put your additional documentation at the top of your `main.tf` for it to be pulled in. The optional `--dest` argument lets you change the name of the file that gets created/modified. + + Example: - 1. Example: ```yaml hooks: - id: terraform_docs_replace args: ['--sort-by-required', '--dest=TEST.md'] ``` -1. It is possible to pass additional arguments to shell scripts when using `terraform_docs` and `terraform_docs_without_aggregate_type_defaults`. Send pull-request with the new hook if there is something missing. +3. It is possible to pass additional arguments to shell scripts when using `terraform_docs` and `terraform_docs_without_aggregate_type_defaults`. Send pull-request with the new hook if there is something missing. -## Notes about terraform_tflint hooks +### terraform_tflint 1. `terraform_tflint` supports custom arguments so you can enable module inspection, deep check mode etc. - 1. Example: + Example: + ```yaml hooks: - id: terraform_tflint @@ -132,6 +176,7 @@ if they are present in `README.md`. ``` In order to pass multiple args, try the following: + ```yaml - id: terraform_tflint args: @@ -139,24 +184,25 @@ if they are present in `README.md`. - '--args=--enable-rule=terraform_documented_variables' ``` -1. When you have multiple directories and want to run `tflint` in all of them and share single config file it is impractical to hard-code the path to `.tflint.hcl` file. The solution is to use `__GIT_WORKING_DIR__` placeholder which will be replaced by `terraform_tflint` hooks with Git working directory (repo root) at run time. For example: +3. When you have multiple directories and want to run `tflint` in all of them and share single config file it is impractical to hard-code the path to `.tflint.hcl` file. The solution is to use `__GIT_WORKING_DIR__` placeholder which will be replaced by `terraform_tflint` hooks with Git working directory (repo root) at run time. For example: - ```yaml - hooks: - - id: terraform_tflint - args: - - '--args=--config=__GIT_WORKING_DIR__/.tflint.hcl' - ``` + ```yaml + hooks: + - id: terraform_tflint + args: + - '--args=--config=__GIT_WORKING_DIR__/.tflint.hcl' + ``` -## Notes about terraform_tfsec hooks +### terraform_tfsec 1. `terraform_tfsec` will consume modified files that pre-commit passes to it, so you can perform whitelisting of directories or files to run against via [files](https://pre-commit.com/#config-files) pre-commit flag - 1. Example: + Example: + ```yaml hooks: - id: terraform_tfsec @@ -167,9 +213,11 @@ if they are present in `README.md`. only such that the underlying `tfsec` tool can run against changed files in this directory, ignoring any other folders at the root level -1. To ignore specific warnings, follow the convention from the +2. To ignore specific warnings, follow the convention from the [documentation](https://github.com/liamg/tfsec#ignoring-warnings). - 1. Example: + + Example: + ```hcl resource "aws_security_group_rule" "my-rule" { type = "ingress" @@ -177,11 +225,12 @@ if they are present in `README.md`. } ``` -## Notes about terraform_validate hooks +### terraform_validate 1. `terraform_validate` supports custom arguments so you can pass supported no-color or json flags. - 1. Example: + Example: + ```yaml hooks: - id: terraform_validate @@ -189,15 +238,18 @@ if they are present in `README.md`. ``` In order to pass multiple args, try the following: + ```yaml - id: terraform_validate args: - '--args=-json' - '--args=-no-color' ``` -1. `terraform_validate` also supports custom environment variables passed to the pre-commit runtime - 1. Example: +2. `terraform_validate` also supports custom environment variables passed to the pre-commit runtime + + Example: + ```yaml hooks: - id: terraform_validate @@ -205,6 +257,7 @@ if they are present in `README.md`. ``` In order to pass multiple args, try the following: + ```yaml - id: terraform_validate args: @@ -213,7 +266,7 @@ if they are present in `README.md`. - '--envs=AWS_SECRET_ACCESS_KEY="asecretkey"' ``` -1. It may happen that Terraform working directory (`.terraform`) already exists but not in the best condition (eg, not initialized modules, wrong version of Terraform, etc). To solve this problem you can find and delete all `.terraform` directories in your repository using this command: +3. It may happen that Terraform working directory (`.terraform`) already exists but not in the best condition (eg, not initialized modules, wrong version of Terraform, etc). To solve this problem you can find and delete all `.terraform` directories in your repository using this command: ```shell find . -type d -name ".terraform" -print0 | xargs -0 rm -r @@ -221,14 +274,29 @@ if they are present in `README.md`. `terraform_validate` hook will try to reinitialize them before running `terraform validate` command. -## Notes for developers +## Notes for contributors 1. Python hooks are supported now too. All you have to do is: 1. add a line to the `console_scripts` array in `entry_points` in `setup.py` - 1. Put your python script in the `pre_commit_hooks` folder + 2. Put your python script in the `pre_commit_hooks` folder Enjoy the clean, valid, and documented code! +### Run and debug hooks locally + +```bash +pre-commit try-repo {-a} /path/to/local/pre-commit-terraform/repo {hook_name} +``` + +I.e. + +```bash +pre-commit try-repo /mnt/c/Users/tf/pre-commit-terraform terraform_fmt # Run only `terraform_fmt` check +pre-commit try-repo -a ~/pre-commit-terraform # run all existing checks from repo +``` + +Running `pre-commit` with `try-repo` ignores all arguments specified in `.pre-commit-config.yaml`. + ## Authors This repository is managed by [Anton Babenko](https://github.com/antonbabenko) with help from [these awesome contributors](https://github.com/antonbabenko/pre-commit-terraform/graphs/contributors). From ce02f94e46635b23087e29e746f0461c6993fee2 Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Thu, 9 Sep 2021 22:29:33 +0300 Subject: [PATCH 125/214] fix: Dockerized pre-commit-terraform (#219) Co-authored-by: Anton Babenko --- Dockerfile | 182 +++++++++++++++++++++++++++++++++++++++++++++-------- README.md | 126 +++++++++++++++++++++++++------------ 2 files changed, 243 insertions(+), 65 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5d3f8906b..239994448 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,36 +1,166 @@ -FROM ubuntu:18.04 - -ARG PRE_COMMIT_VERSION="2.11.1" -ARG TERRAFORM_VERSION="0.15.0" -ARG TFSEC_VERSION="v0.39.21" -ARG TERRAFORM_DOCS_VERSION="v0.12.0" -ARG TFLINT_VERSION="v0.27.0" -ARG CHECKOV_VERSION="1.0.838" +FROM ubuntu:20.04 as builder # Install general dependencies RUN apt update && \ - apt install -y curl git gawk unzip software-properties-common + DEBIAN_FRONTEND=noninteractive apt install -y \ + # Needed for pre-commit in next build stage + git \ + libpcre2-8-0 \ + # Builder deps + unzip \ + software-properties-common \ + curl \ + python3 \ + python3-pip && \ + # Upgrade pip for be able get latest Checkov + python3 -m pip install --upgrade pip && \ + # Cleanup + rm -rf /var/lib/apt/lists/* -# Install tools -RUN add-apt-repository ppa:deadsnakes/ppa && \ - apt install -y python3.7 python3-pip && \ - pip3 install pre-commit==${PRE_COMMIT_VERSION} && \ - curl -L "$(curl -s https://api.github.com/repos/terraform-docs/terraform-docs/releases | grep -o -E "https://.+?${TERRAFORM_DOCS_VERSION}-linux-amd64.tar.gz")" > terraform-docs.tgz && tar xzf terraform-docs.tgz && chmod +x terraform-docs && mv terraform-docs /usr/bin/ && \ - curl -L "$(curl -s https://api.github.com/repos/terraform-linters/tflint/releases | grep -o -E "https://.+?/${TFLINT_VERSION}/tflint_linux_amd64.zip")" > tflint.zip && unzip tflint.zip && rm tflint.zip && mv tflint /usr/bin/ && \ - curl -L "$(curl -s https://api.github.com/repos/tfsec/tfsec/releases | grep -o -E "https://.+?/${TFSEC_VERSION}/tfsec-linux-amd64")" > tfsec && chmod +x tfsec && mv tfsec /usr/bin/ && \ - python3.7 -m pip install -U checkov==${CHECKOV_VERSION} +ARG PRE_COMMIT_VERSION=${PRE_COMMIT_VERSION:-latest} +ARG TERRAFORM_VERSION=${TERRAFORM_VERSION:-latest} + +# Install pre-commit +RUN [ ${PRE_COMMIT_VERSION} = "latest" ] && pip3 install --no-cache-dir pre-commit \ + || pip3 install --no-cache-dir pre-commit==${PRE_COMMIT_VERSION} # Install terraform because pre-commit needs it RUN curl -fsSL https://apt.releases.hashicorp.com/gpg | apt-key add - && \ apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main" && \ - apt-get update && apt-get install terraform=${TERRAFORM_VERSION} - -# Checking all binaries are in the PATH -RUN terraform --help -RUN pre-commit --help -RUN terraform-docs --help -RUN tflint --help -RUN tfsec --help -RUN checkov --help + apt update && \ + ( \ + [ "$TERRAFORM_VERSION" = "latest" ] && apt install -y terraform \ + || apt install -y terraform=${TERRAFORM_VERSION} \ + ) && \ + # Cleanup + rm -rf /var/lib/apt/lists/* + +# +# Install tools +# +WORKDIR /bin_dir + +ARG CHECKOV_VERSION=${CHECKOV_VERSION:-false} +ARG TERRAFORM_DOCS_VERSION=${TERRAFORM_DOCS_VERSION:-false} +ARG TERRAGRUNT_VERSION=${TERRAGRUNT_VERSION:-false} +ARG TERRASCAN_VERSION=${TERRASCAN_VERSION:-false} +ARG TFLINT_VERSION=${TFLINT_VERSION:-false} +ARG TFSEC_VERSION=${TFSEC_VERSION:-false} + + +# Tricky thing to install all tools by set only one arg. +# In RUN command below used `. /.env` <- this is sourcing vars that +# specified in step below +ARG INSTALL_ALL=${INSTALL_ALL:-false} +RUN if [ "$INSTALL_ALL" != "false" ]; then \ + echo "export CHECKOV_VERSION=latest" >> /.env && \ + echo "export TERRAFORM_DOCS_VERSION=latest" >> /.env && \ + echo "export TERRAGRUNT_VERSION=latest" >> /.env && \ + echo "export TERRASCAN_VERSION=latest" >> /.env && \ + echo "export TFLINT_VERSION=latest" >> /.env && \ + echo "export TFSEC_VERSION=latest" >> /.env \ + ; fi + + +# Checkov +RUN . /.env && \ + if [ "$CHECKOV_VERSION" != "false" ]; then \ + ( \ + [ "$CHECKOV_VERSION" = "latest" ] && pip3 install --no-cache-dir checkov \ + || pip3 install --no-cache-dir checkov==${CHECKOV_VERSION} \ + ) \ + ; fi + +# Terraform docs +RUN . /.env && \ + if [ "$TERRAFORM_DOCS_VERSION" != "false" ]; then \ + ( \ + TERRAFORM_DOCS_RELEASES="https://api.github.com/repos/terraform-docs/terraform-docs/releases" && \ + [ "$TERRAFORM_DOCS_VERSION" = "latest" ] && curl -L "$(curl -s ${TERRAFORM_DOCS_RELEASES}/latest | grep -o -E "https://.+?-linux-amd64.tar.gz")" > terraform-docs.tgz \ + || curl -L "$(curl -s ${TERRAFORM_DOCS_RELEASES} | grep -o -E "https://.+?v${TERRAFORM_DOCS_VERSION}-linux-amd64.tar.gz")" > terraform-docs.tgz \ + ) && tar -xzf terraform-docs.tgz terraform-docs && chmod +x terraform-docs \ + ; fi + +# Terragrunt +RUN . /.env \ + && if [ "$TERRAGRUNT_VERSION" != "false" ]; then \ + ( \ + TERRAGRUNT_RELEASES="https://api.github.com/repos/gruntwork-io/terragrunt/releases" && \ + [ "$TERRAGRUNT_VERSION" = "latest" ] && curl -L "$(curl -s ${TERRAGRUNT_RELEASES}/latest | grep -o -E "https://.+?/terragrunt_linux_amd64" | head -n 1)" > terragrunt \ + || curl -L "$(curl -s ${TERRAGRUNT_RELEASES} | grep -o -E "https://.+?v${TERRAGRUNT_VERSION}/terragrunt_linux_amd64" | head -n 1)" > terragrunt \ + ) && chmod +x terragrunt \ + ; fi + + +# Terrascan +RUN . /.env && \ + if [ "$TERRASCAN_VERSION" != "false" ]; then \ + ( \ + TERRASCAN_RELEASES="https://api.github.com/repos/accurics/terrascan/releases" && \ + [ "$TERRASCAN_VERSION" = "latest" ] && curl -L "$(curl -s ${TERRASCAN_RELEASES}/latest | grep -o -E "https://.+?_Linux_x86_64.tar.gz")" > terrascan.tar.gz \ + || curl -L "$(curl -s ${TERRASCAN_RELEASES} | grep -o -E "https://.+?${TERRASCAN_VERSION}_Linux_x86_64.tar.gz")" > terrascan.tar.gz \ + ) && tar -xzf terrascan.tar.gz terrascan && rm terrascan.tar.gz && \ + ./terrascan init \ + ; fi + +# TFLint +RUN . /.env && \ + if [ "$TFLINT_VERSION" != "false" ]; then \ + ( \ + TFLINT_RELEASES="https://api.github.com/repos/terraform-linters/tflint/releases" && \ + [ "$TFLINT_VERSION" = "latest" ] && curl -L "$(curl -s ${TFLINT_RELEASES}/latest | grep -o -E "https://.+?_linux_amd64.zip")" > tflint.zip \ + || curl -L "$(curl -s ${TFLINT_RELEASES} | grep -o -E "https://.+?/v${TFLINT_VERSION}/tflint_linux_amd64.zip")" > tflint.zip \ + ) && unzip tflint.zip && rm tflint.zip \ + ; fi + +# TFSec +RUN . /.env && \ + if [ "$TFSEC_VERSION" != "false" ]; then \ + ( \ + TFSEC_RELEASES="https://api.github.com/repos/aquasecurity/tfsec/releases" && \ + [ "$TFSEC_VERSION" = "latest" ] && curl -L "$(curl -s ${TFSEC_RELEASES}/latest | grep -o -E "https://.+?/tfsec-linux-amd64" | head -n 1)" > tfsec \ + || curl -L "$(curl -s ${TFSEC_RELEASES} | grep -o -E "https://.+?v${TFSEC_VERSION}/tfsec-linux-amd64" | head -n 1)" > tfsec \ + ) && chmod +x tfsec \ + ; fi + +# Checking binaries versions +RUN . /.env && \ + echo "\n\n" && \ + pre-commit --version && \ + terraform --version | head -n 1 && \ + (if [ "$CHECKOV_VERSION" != "false" ]; then echo -n "checkov " && checkov --version; else echo "checkov SKIPPED" ; fi) && \ + (if [ "$TERRAFORM_DOCS_VERSION" != "false" ]; then ./terraform-docs --version; else echo "terraform-docs SKIPPED"; fi) && \ + (if [ "$TERRAGRUNT_VERSION" != "false" ]; then ./terragrunt --version; else echo "terragrunt SKIPPED" ; fi) && \ + (if [ "$TERRASCAN_VERSION" != "false" ]; then echo -n "terrascan " && ./terrascan version; else echo "terrascan SKIPPED" ; fi) && \ + (if [ "$TFLINT_VERSION" != "false" ]; then ./tflint --version; else echo "tflint SKIPPED" ; fi) && \ + (if [ "$TFSEC_VERSION" != "false" ]; then echo -n "tfsec " && ./tfsec --version; else echo "tfsec SKIPPED" ; fi) && \ + echo "\n\n" + +# based on debian:buster-slim +# https://github.com/docker-library/python/blob/master/3.9/buster/slim/Dockerfile +FROM python:3.9-slim-buster + +# Python 3.8 (ubuntu 20.04) -> Python3.9 hacks +COPY --from=builder /usr/local/lib/python3.8/dist-packages/ /usr/local/lib/python3.9/site-packages/ +COPY --from=builder /usr/lib/python3/dist-packages /usr/local/lib/python3.9/site-packages +RUN mkdir /usr/lib/python3 && \ + ln -s /usr/local/lib/python3.9/site-packages /usr/lib/python3/site-packages && \ + ln -s /usr/local/bin/python3 /usr/bin/python3 +# Copy binaries needed for pre-commit +COPY --from=builder /usr/lib/git-core/ /usr/lib/git-core/ +COPY --from=builder /usr/lib/x86_64-linux-gnu/libpcre2-8.so.0 /usr/lib/x86_64-linux-gnu/ +# Copy tools +COPY --from=builder \ + /bin_dir/ \ + /usr/bin/terraform \ + /usr/local/bin/checkov* \ + /usr/local/bin/pre-commit \ + /usr/bin/git \ + /usr/bin/git-shell \ + /usr/bin/ +# Copy terrascan policies +COPY --from=builder /root/ /root/ + +ENV PRE_COMMIT_COLOR=${PRE_COMMIT_COLOR:-always} ENTRYPOINT [ "pre-commit" ] diff --git a/README.md b/README.md index 8852df7d1..19901f3d9 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,6 @@ * [How to install](#how-to-install) * [1. Install dependencies](#1-install-dependencies) - * [MacOS](#macos) - * [Ubuntu 18.04](#ubuntu-1804) - * [Ubuntu 20.04](#ubuntu-2004) * [2. Install the pre-commit hook globally](#2-install-the-pre-commit-hook-globally) * [3. Add configs and hooks](#3-add-configs-and-hooks) * [4. Run](#4-run) @@ -25,23 +22,69 @@ ### 1. Install dependencies -* [`pre-commit`](https://pre-commit.com/#install) -* [`terraform-docs`](https://github.com/terraform-docs/terraform-docs) required for `terraform_docs` hooks. `GNU awk` is required if using `terraform-docs` older than 0.8.0 with Terraform 0.12. -* [`TFLint`](https://github.com/terraform-linters/tflint) required for `terraform_tflint` hook. -* [`TFSec`](https://github.com/liamg/tfsec) required for `terraform_tfsec` hook. -* [`coreutils`](https://formulae.brew.sh/formula/coreutils) required for `terraform_validate` hook on macOS (due to use of `realpath`). + + +* [`pre-commit`](https://pre-commit.com/#install), + [`terraform`](https://www.terraform.io/downloads.html), + [`git`](https://git-scm.com/downloads), + POSIX compatible shell, + Internet connection (on first run), + x86_64 compatible operation system, + Some hardware where this OS will run, + Electricity for hardware and internet connection, + Some basic physical laws, + Hope that it all will works. +

* [`checkov`](https://github.com/bridgecrewio/checkov) required for `checkov` hook. +* [`terraform-docs`](https://github.com/terraform-docs/terraform-docs) required for `terraform_docs` hooks. * [`terrascan`](https://github.com/accurics/terrascan) required for `terrascan` hook. +* [`TFLint`](https://github.com/terraform-linters/tflint) required for `terraform_tflint` hook. +* [`TFSec`](https://github.com/liamg/tfsec) required for `terraform_tfsec` hook. + +
Docker
+ +If no `--build-arg` is specified, then the latest versions of `pre-commit` and `terraform` will be installed. + +```bash +git clone git@github.com:antonbabenko/pre-commit-terraform.git +cd pre-commit-terraform +# Install all tools with latest versions: +docker build -t pre-commit --build-arg INSTALL_ALL=true . +``` + +You can specify needed tool versions by providing `--build-arg`'s. +If you'd like you can use the `latest` versions: + +```bash +docker build -t pre-commit \ + --build-arg PRE_COMMIT_VERSION=latest \ + --build-arg TERRAFORM_VERSION=latest \ + --build-arg CHECKOV_VERSION=2.0.405 \ + --build-arg TERRAFORM_DOCS_VERSION=0.15.0 \ + --build-arg TERRAGRUNT_VERSION=latest \ + --build-arg TERRASCAN_VERSION=1.10.0 \ + --build-arg TFLINT_VERSION=0.31.0 \ + --build-arg TFSEC_VERSION=latest \ + . +``` -or build and use the Docker image locally as mentioned below in the `Run` section. +To disable pre-commit color output set `-e PRE_COMMIT_COLOR=never`. -#### MacOS +
+ + +
MacOS
+ +[`coreutils`](https://formulae.brew.sh/formula/coreutils) required for `terraform_validate` hook on macOS (due to use of `realpath`). ```bash brew install pre-commit gawk terraform-docs tflint tfsec coreutils checkov terrascan +terrascan init ``` -#### Ubuntu 18.04 +
+ +
Ubuntu 18.04
```bash sudo apt update @@ -49,33 +92,40 @@ sudo apt install -y gawk unzip software-properties-common sudo add-apt-repository ppa:deadsnakes/ppa sudo apt install -y python3.7 python3-pip python3 -m pip install --upgrade pip -pip3 install pre-commit +pip3 install --no-cache-dir pre-commit +python3.7 -m pip install -U checkov curl -L "$(curl -s https://api.github.com/repos/terraform-docs/terraform-docs/releases/latest | grep -o -E "https://.+?-linux-amd64.tar.gz")" > terraform-docs.tgz && tar xzf terraform-docs.tgz && chmod +x terraform-docs && sudo mv terraform-docs /usr/bin/ curl -L "$(curl -s https://api.github.com/repos/terraform-linters/tflint/releases/latest | grep -o -E "https://.+?_linux_amd64.zip")" > tflint.zip && unzip tflint.zip && rm tflint.zip && sudo mv tflint /usr/bin/ curl -L "$(curl -s https://api.github.com/repos/aquasecurity/tfsec/releases/latest | grep -o -E "https://.+?tfsec-linux-amd64" | head -n 1)" > tfsec && chmod +x tfsec && sudo mv tfsec /usr/bin/ curl -L "$(curl -s https://api.github.com/repos/accurics/terrascan/releases/latest | grep -o -E "https://.+?_Linux_x86_64.tar.gz")" > terrascan.tar.gz && tar -xf terrascan.tar.gz terrascan && rm terrascan.tar.gz && sudo mv terrascan /usr/bin/ -python3.7 -m pip install -U checkov +terrascan init ``` -##### Ubuntu 20.04 +
+ + +
Ubuntu 20.04
```bash sudo apt update -sudo apt install -y gawk unzip software-properties-common -sudo apt install -y python3 python3-pip +sudo apt install -y gawk unzip software-properties-common python3 python3-pip python3 -m pip install --upgrade pip -pip3 install pre-commit +pip3 install --no-cache-dir pre-commit +pip3 install --no-cache-dir checkov curl -L "$(curl -s https://api.github.com/repos/terraform-docs/terraform-docs/releases/latest | grep -o -E "https://.+?-linux-amd64.tar.gz")" > terraform-docs.tgz && tar -xzf terraform-docs.tgz terraform-docs && chmod +x terraform-docs && sudo mv terraform-docs /usr/bin/ +curl -L "$(curl -s https://api.github.com/repos/accurics/terrascan/releases/latest | grep -o -E "https://.+?_Linux_x86_64.tar.gz")" > terrascan.tar.gz && tar -xf terrascan.tar.gz terrascan && rm terrascan.tar.gz && sudo mv terrascan /usr/bin/ +terrascan init curl -L "$(curl -s https://api.github.com/repos/terraform-linters/tflint/releases/latest | grep -o -E "https://.+?_linux_amd64.zip")" > tflint.zip && unzip tflint.zip && rm tflint.zip && sudo mv tflint /usr/bin/ curl -L "$(curl -s https://api.github.com/repos/aquasecurity/tfsec/releases/latest | grep -o -E "https://.+?tfsec-linux-amd64" | head -n 1)" > tfsec && chmod +x tfsec && sudo mv tfsec /usr/bin/ -curl -L "$(curl -s https://api.github.com/repos/accurics/terrascan/releases/latest | grep -o -E "https://.+?_Linux_x86_64.tar.gz")" > terrascan.tar.gz && tar -xf terrascan.tar.gz terrascan && rm terrascan.tar.gz && sudo mv terrascan /usr/bin/ -pip3 install -U checkov ``` +
+ + ### 2. Install the pre-commit hook globally -Note: not needed if you use the Docker image +> Note: not needed if you use the Docker image ```bash DIR=~/.git-template @@ -101,19 +151,17 @@ EOF ### 4. Run -After pre-commit hook has been installed you can run it manually on all files in the repository +After pre-commit hook has been installed you can run it manually on all files in the repository. + +Local installation: ```bash pre-commit run -a ``` -or you can also build and use the provided Docker container, which wraps all dependencies by +Docker: ```bash -# first building it -docker build -t pre-commit . -# and then running it in the folder -# with the terraform code you want to check by executing docker run -v $(pwd):/lint -w /lint pre-commit run -a ``` @@ -121,19 +169,19 @@ docker run -v $(pwd):/lint -w /lint pre-commit run -a There are several [pre-commit](https://pre-commit.com/) hooks to keep Terraform configurations (both `*.tf` and `*.tfvars`) and Terragrunt configurations (`*.hcl`) in a good shape: -| Hook name | Description | -| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ | -| `terraform_fmt` | Rewrites all Terraform configuration files to a canonical format. [Hook notes](#terraform_docs) | -| `terraform_validate` | Validates all Terraform configuration files. [Hook notes](#terraform_validate) | -| `terraform_docs` | Inserts input and output documentation into `README.md`. Recommended. | -| `terraform_docs_without_aggregate_type_defaults` | Inserts input and output documentation into `README.md` without aggregate type defaults. | -| `terraform_docs_replace` | Runs `terraform-docs` and pipes the output directly to README.md | -| `terraform_tflint` | Validates all Terraform configuration files with [TFLint](https://github.com/terraform-linters/tflint). [Hook notes](#terraform_tflint). | -| `terragrunt_fmt` | Rewrites all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) to a canonical format. | -| `terragrunt_validate` | Validates all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) | -| `terraform_tfsec` | [TFSec](https://github.com/liamg/tfsec) static analysis of terraform templates to spot potential security issues. [Hook notes](#terraform_tfsec) | -| `checkov` | [checkov](https://github.com/bridgecrewio/checkov) static analysis of terraform templates to spot potential security issues. | -| `terrascan` | [terrascan](https://github.com/accurics/terrascan) Detect compliance and security violations. | +| Hook name | Description | +| ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `terraform_fmt` | Rewrites all Terraform configuration files to a canonical format. [Hook notes](#terraform_docs) | +| `terraform_validate` | Validates all Terraform configuration files. [Hook notes](#terraform_validate) | +| `terraform_docs` | Inserts input and output documentation into `README.md`. Recommended. | +| `terraform_docs_without_aggregate_type_defaults` | Inserts input and output documentation into `README.md` without aggregate type defaults. | +| `terraform_docs_replace` | Runs `terraform-docs` and pipes the output directly to README.md | +| `terraform_tflint` | Validates all Terraform configuration files with [TFLint](https://github.com/terraform-linters/tflint). [Available TFLint rules](https://github.com/terraform-linters/tflint/tree/master/docs/rules#rules). [Hook notes](#terraform_tflint). | +| `terragrunt_fmt` | Rewrites all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) to a canonical format. | +| `terragrunt_validate` | Validates all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) | +| `terraform_tfsec` | [TFSec](https://github.com/liamg/tfsec) static analysis of terraform templates to spot potential security issues. [Hook notes](#terraform_tfsec) | +| `checkov` | [checkov](https://github.com/bridgecrewio/checkov) static analysis of terraform templates to spot potential security issues. | +| `terrascan` | [terrascan](https://github.com/accurics/terrascan) Detect compliance and security violations. | Check the [source file](https://github.com/antonbabenko/pre-commit-terraform/blob/master/.pre-commit-hooks.yaml) to know arguments used for each hook. From c920368fb2485e0dde8cd9956adb2cf9fa230da0 Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Fri, 10 Sep 2021 22:33:03 +0300 Subject: [PATCH 126/214] feat: Add mixed line ending check to prevent possible errors (#221) --- .pre-commit-config.yaml | 2 ++ Dockerfile | 16 ++++++++-------- README.md | 24 +++++++++++------------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 354e538dc..020f65f4c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,6 +19,8 @@ repos: # Cross platform - id: check-case-conflict + - id: mixed-line-ending + args: [--fix=lf] # Security - id: detect-aws-credentials diff --git a/Dockerfile b/Dockerfile index 239994448..5fa7552ac 100644 --- a/Dockerfile +++ b/Dockerfile @@ -76,9 +76,9 @@ RUN . /.env && \ if [ "$TERRAFORM_DOCS_VERSION" != "false" ]; then \ ( \ TERRAFORM_DOCS_RELEASES="https://api.github.com/repos/terraform-docs/terraform-docs/releases" && \ - [ "$TERRAFORM_DOCS_VERSION" = "latest" ] && curl -L "$(curl -s ${TERRAFORM_DOCS_RELEASES}/latest | grep -o -E "https://.+?-linux-amd64.tar.gz")" > terraform-docs.tgz \ + [ "$TERRAFORM_DOCS_VERSION" = "latest" ] && curl -L "$(curl -s ${TERRAFORM_DOCS_RELEASES}/latest | grep -o -E -m 1 "https://.+?-linux-amd64.tar.gz")" > terraform-docs.tgz \ || curl -L "$(curl -s ${TERRAFORM_DOCS_RELEASES} | grep -o -E "https://.+?v${TERRAFORM_DOCS_VERSION}-linux-amd64.tar.gz")" > terraform-docs.tgz \ - ) && tar -xzf terraform-docs.tgz terraform-docs && chmod +x terraform-docs \ + ) && tar -xzf terraform-docs.tgz terraform-docs && rm terraform-docs.tgz && chmod +x terraform-docs \ ; fi # Terragrunt @@ -86,8 +86,8 @@ RUN . /.env \ && if [ "$TERRAGRUNT_VERSION" != "false" ]; then \ ( \ TERRAGRUNT_RELEASES="https://api.github.com/repos/gruntwork-io/terragrunt/releases" && \ - [ "$TERRAGRUNT_VERSION" = "latest" ] && curl -L "$(curl -s ${TERRAGRUNT_RELEASES}/latest | grep -o -E "https://.+?/terragrunt_linux_amd64" | head -n 1)" > terragrunt \ - || curl -L "$(curl -s ${TERRAGRUNT_RELEASES} | grep -o -E "https://.+?v${TERRAGRUNT_VERSION}/terragrunt_linux_amd64" | head -n 1)" > terragrunt \ + [ "$TERRAGRUNT_VERSION" = "latest" ] && curl -L "$(curl -s ${TERRAGRUNT_RELEASES}/latest | grep -o -E -m 1 "https://.+?/terragrunt_linux_amd64")" > terragrunt \ + || curl -L "$(curl -s ${TERRAGRUNT_RELEASES} | grep -o -E -m 1 "https://.+?v${TERRAGRUNT_VERSION}/terragrunt_linux_amd64")" > terragrunt \ ) && chmod +x terragrunt \ ; fi @@ -97,7 +97,7 @@ RUN . /.env && \ if [ "$TERRASCAN_VERSION" != "false" ]; then \ ( \ TERRASCAN_RELEASES="https://api.github.com/repos/accurics/terrascan/releases" && \ - [ "$TERRASCAN_VERSION" = "latest" ] && curl -L "$(curl -s ${TERRASCAN_RELEASES}/latest | grep -o -E "https://.+?_Linux_x86_64.tar.gz")" > terrascan.tar.gz \ + [ "$TERRASCAN_VERSION" = "latest" ] && curl -L "$(curl -s ${TERRASCAN_RELEASES}/latest | grep -o -E -m 1 "https://.+?_Linux_x86_64.tar.gz")" > terrascan.tar.gz \ || curl -L "$(curl -s ${TERRASCAN_RELEASES} | grep -o -E "https://.+?${TERRASCAN_VERSION}_Linux_x86_64.tar.gz")" > terrascan.tar.gz \ ) && tar -xzf terrascan.tar.gz terrascan && rm terrascan.tar.gz && \ ./terrascan init \ @@ -108,7 +108,7 @@ RUN . /.env && \ if [ "$TFLINT_VERSION" != "false" ]; then \ ( \ TFLINT_RELEASES="https://api.github.com/repos/terraform-linters/tflint/releases" && \ - [ "$TFLINT_VERSION" = "latest" ] && curl -L "$(curl -s ${TFLINT_RELEASES}/latest | grep -o -E "https://.+?_linux_amd64.zip")" > tflint.zip \ + [ "$TFLINT_VERSION" = "latest" ] && curl -L "$(curl -s ${TFLINT_RELEASES}/latest | grep -o -E -m 1 "https://.+?_linux_amd64.zip")" > tflint.zip \ || curl -L "$(curl -s ${TFLINT_RELEASES} | grep -o -E "https://.+?/v${TFLINT_VERSION}/tflint_linux_amd64.zip")" > tflint.zip \ ) && unzip tflint.zip && rm tflint.zip \ ; fi @@ -118,8 +118,8 @@ RUN . /.env && \ if [ "$TFSEC_VERSION" != "false" ]; then \ ( \ TFSEC_RELEASES="https://api.github.com/repos/aquasecurity/tfsec/releases" && \ - [ "$TFSEC_VERSION" = "latest" ] && curl -L "$(curl -s ${TFSEC_RELEASES}/latest | grep -o -E "https://.+?/tfsec-linux-amd64" | head -n 1)" > tfsec \ - || curl -L "$(curl -s ${TFSEC_RELEASES} | grep -o -E "https://.+?v${TFSEC_VERSION}/tfsec-linux-amd64" | head -n 1)" > tfsec \ + [ "$TFSEC_VERSION" = "latest" ] && curl -L "$(curl -s ${TFSEC_RELEASES}/latest | grep -o -E -m 1 "https://.+?/tfsec-linux-amd64")" > tfsec \ + || curl -L "$(curl -s ${TFSEC_RELEASES} | grep -o -E -m 1 "https://.+?v${TFSEC_VERSION}/tfsec-linux-amd64")" > tfsec \ ) && chmod +x tfsec \ ; fi diff --git a/README.md b/README.md index 19901f3d9..aeeb9668c 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ To disable pre-commit color output set `-e PRE_COMMIT_COLOR=never`. [`coreutils`](https://formulae.brew.sh/formula/coreutils) required for `terraform_validate` hook on macOS (due to use of `realpath`). ```bash -brew install pre-commit gawk terraform-docs tflint tfsec coreutils checkov terrascan +brew install pre-commit terraform-docs tflint tfsec coreutils checkov terrascan terrascan init ``` @@ -88,17 +88,16 @@ terrascan init ```bash sudo apt update -sudo apt install -y gawk unzip software-properties-common +sudo apt install -y unzip software-properties-common sudo add-apt-repository ppa:deadsnakes/ppa sudo apt install -y python3.7 python3-pip python3 -m pip install --upgrade pip pip3 install --no-cache-dir pre-commit python3.7 -m pip install -U checkov -curl -L "$(curl -s https://api.github.com/repos/terraform-docs/terraform-docs/releases/latest | grep -o -E "https://.+?-linux-amd64.tar.gz")" > terraform-docs.tgz && tar xzf terraform-docs.tgz && chmod +x terraform-docs && sudo mv terraform-docs /usr/bin/ -curl -L "$(curl -s https://api.github.com/repos/terraform-linters/tflint/releases/latest | grep -o -E "https://.+?_linux_amd64.zip")" > tflint.zip && unzip tflint.zip && rm tflint.zip && sudo mv tflint /usr/bin/ -curl -L "$(curl -s https://api.github.com/repos/aquasecurity/tfsec/releases/latest | grep -o -E "https://.+?tfsec-linux-amd64" | head -n 1)" > tfsec && chmod +x tfsec && sudo mv tfsec /usr/bin/ -curl -L "$(curl -s https://api.github.com/repos/accurics/terrascan/releases/latest | grep -o -E "https://.+?_Linux_x86_64.tar.gz")" > terrascan.tar.gz && tar -xf terrascan.tar.gz terrascan && rm terrascan.tar.gz && sudo mv terrascan /usr/bin/ -terrascan init +curl -L "$(curl -s https://api.github.com/repos/terraform-docs/terraform-docs/releases/latest | grep -o -E -m 1 "https://.+?-linux-amd64.tar.gz")" > terraform-docs.tgz && tar -xzf terraform-docs.tgz && rm terraform-docs.tgz && chmod +x terraform-docs && sudo mv terraform-docs /usr/bin/ +curl -L "$(curl -s https://api.github.com/repos/terraform-linters/tflint/releases/latest | grep -o -E -m 1 "https://.+?_linux_amd64.zip")" > tflint.zip && unzip tflint.zip && rm tflint.zip && sudo mv tflint /usr/bin/ +curl -L "$(curl -s https://api.github.com/repos/aquasecurity/tfsec/releases/latest | grep -o -E -m 1 "https://.+?tfsec-linux-amd64")" > tfsec && chmod +x tfsec && sudo mv tfsec /usr/bin/ +curl -L "$(curl -s https://api.github.com/repos/accurics/terrascan/releases/latest | grep -o -E -m 1"https://.+?_Linux_x86_64.tar.gz")" > terrascan.tar.gz && tar -xzf terrascan.tar.gz terrascan && rm terrascan.tar.gz && sudo mv terrascan /usr/bin/ && terrascan init ``` @@ -108,15 +107,14 @@ terrascan init ```bash sudo apt update -sudo apt install -y gawk unzip software-properties-common python3 python3-pip +sudo apt install -y unzip software-properties-common python3 python3-pip python3 -m pip install --upgrade pip pip3 install --no-cache-dir pre-commit pip3 install --no-cache-dir checkov -curl -L "$(curl -s https://api.github.com/repos/terraform-docs/terraform-docs/releases/latest | grep -o -E "https://.+?-linux-amd64.tar.gz")" > terraform-docs.tgz && tar -xzf terraform-docs.tgz terraform-docs && chmod +x terraform-docs && sudo mv terraform-docs /usr/bin/ -curl -L "$(curl -s https://api.github.com/repos/accurics/terrascan/releases/latest | grep -o -E "https://.+?_Linux_x86_64.tar.gz")" > terrascan.tar.gz && tar -xf terrascan.tar.gz terrascan && rm terrascan.tar.gz && sudo mv terrascan /usr/bin/ -terrascan init -curl -L "$(curl -s https://api.github.com/repos/terraform-linters/tflint/releases/latest | grep -o -E "https://.+?_linux_amd64.zip")" > tflint.zip && unzip tflint.zip && rm tflint.zip && sudo mv tflint /usr/bin/ -curl -L "$(curl -s https://api.github.com/repos/aquasecurity/tfsec/releases/latest | grep -o -E "https://.+?tfsec-linux-amd64" | head -n 1)" > tfsec && chmod +x tfsec && sudo mv tfsec /usr/bin/ +curl -L "$(curl -s https://api.github.com/repos/terraform-docs/terraform-docs/releases/latest | grep -o -E -m 1 "https://.+?-linux-amd64.tar.gz")" > terraform-docs.tgz && tar -xzf terraform-docs.tgz terraform-docs && rm terraform-docs.tgz && chmod +x terraform-docs && sudo mv terraform-docs /usr/bin/ +curl -L "$(curl -s https://api.github.com/repos/accurics/terrascan/releases/latest | grep -o -E -m 1"https://.+?_Linux_x86_64.tar.gz")" > terrascan.tar.gz && tar -xzf terrascan.tar.gz terrascan && rm terrascan.tar.gz && sudo mv terrascan /usr/bin/ && terrascan init +curl -L "$(curl -s https://api.github.com/repos/terraform-linters/tflint/releases/latest | grep -o -E -m 1 "https://.+?_linux_amd64.zip")" > tflint.zip && unzip tflint.zip && rm tflint.zip && sudo mv tflint /usr/bin/ +curl -L "$(curl -s https://api.github.com/repos/aquasecurity/tfsec/releases/latest | grep -o -E -m 1 "https://.+?tfsec-linux-amd64")" > tfsec && chmod +x tfsec && sudo mv tfsec /usr/bin/ ``` From 53a866e7752885b8854a413fd76e397677050d4c Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Sat, 11 Sep 2021 10:47:56 +0300 Subject: [PATCH 127/214] feat: Add GH checks and templates (#222) --- .github/ISSUE_TEMPLATE/bug_report_docker.md | 81 +++++++++++++ .../bug_report_local_install.md | 106 ++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 29 +++++ .github/PULL_REQUEST_TEMPLATE.md | 31 +++++ .github/workflows/pre-commit.yaml | 27 +++++ .github/workflows/stale-actions.yaml | 34 ++++++ Dockerfile | 22 ++-- README.md | 7 ++ 8 files changed, 326 insertions(+), 11 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug_report_docker.md create mode 100644 .github/ISSUE_TEMPLATE/bug_report_local_install.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/workflows/pre-commit.yaml create mode 100644 .github/workflows/stale-actions.yaml diff --git a/.github/ISSUE_TEMPLATE/bug_report_docker.md b/.github/ISSUE_TEMPLATE/bug_report_docker.md new file mode 100644 index 000000000..f1cddc738 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report_docker.md @@ -0,0 +1,81 @@ +--- +name: Local installation bug report +about: Create a bug report +labels: +- kind/bug +- area/docker +--- + + + +### Describe the bug + + + + +### How can we reproduce it? + + + + +### Environment information + +* OS: + + + +* `docker info`: + +
command output + +```bash +INSERT_OUTPUT_HERE +``` + +
+ +* Docker image tag/git commit: + +* Tools versions. Don't forget to specify right tag in command - + `TAG=latest && docker run --entrypoint cat pre-commit:$TAG /usr/bin/tools_versions_info` + +```bash +INSERT_OUTPUT_HERE +``` + +* `.pre-commit-config.yaml`: + +
file content + +```bash +INSERT_FILE_CONTENT_HERE +``` + +
diff --git a/.github/ISSUE_TEMPLATE/bug_report_local_install.md b/.github/ISSUE_TEMPLATE/bug_report_local_install.md new file mode 100644 index 000000000..f0798db1f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report_local_install.md @@ -0,0 +1,106 @@ +--- +name: Docker bug report +about: Create a bug report +labels: +- kind/bug +- area/local_installation +--- + + + +### Describe the bug + + + + +### How can we reproduce it? + + + + +### Environment information + +* OS: + + +* `uname -a` and/or `systeminfo | Select-String "^OS"` output: + +```bash +INSERT_OUTPUT_HERE +``` + + + +* Tools availability and versions: + + + +```bash +INSERT_TOOLS_VERSIONS_HERE +``` + + +* `.pre-commit-config.yaml`: + +
file content + +```bash +INSERT_FILE_CONTENT_HERE +``` + +
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..8d9f73189 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,29 @@ +--- +name: Feature request +about: Suggest an idea for this project +labels: +- kind/feature +--- + + + +### What problem are you facing? + + + + +### How could pre-commit-terraform help solve your problem? + + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..a7af18c5b --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,31 @@ + + +Put an `x` into the box if that apply: + +- [ ] This PR introduces breaking change. +- [ ] This PR fixes a bug. +- [ ] This PR adds new functionality. +- [ ] This PR enhances existing functionality. + +### Description of your changes + + + + + +### How has this code been tested + + diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml new file mode 100644 index 000000000..773ff8e09 --- /dev/null +++ b/.github/workflows/pre-commit.yaml @@ -0,0 +1,27 @@ +name: Common issues check + +on: [pull_request] + +jobs: + pre-commit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: | + git fetch --no-tags --prune --depth=1 origin +refs/heads/*:refs/remotes/origin/* + - name: Get changed files + id: file_changes + run: | + export DIFF=$(git diff --name-only origin/${{ github.base_ref }} ${{ github.sha }}) + echo "Diff between ${{ github.base_ref }} and ${{ github.sha }}" + echo "::set-output name=files::$( echo "$DIFF" | xargs echo )" + - uses: actions/setup-python@v2 + with: + python-version: '3.9' + - name: Execute pre-commit + uses: pre-commit/action@v2.0.0 + env: + SKIP: no-commit-to-branch + with: + token: ${{ secrets.GITHUB_TOKEN }} + extra_args: --color=always --show-diff-on-failure --files ${{ steps.file_changes.outputs.files }} diff --git a/.github/workflows/stale-actions.yaml b/.github/workflows/stale-actions.yaml new file mode 100644 index 000000000..0000f4075 --- /dev/null +++ b/.github/workflows/stale-actions.yaml @@ -0,0 +1,34 @@ +name: "Mark or close stale issues and PRs" +on: + schedule: + - cron: "0 0 * * *" + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v3 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + # Staling issues and PR's + days-before-stale: 30 + stale-issue-label: lifecycle/stale + stale-pr-label: lifecycle/stale + stale-issue-message: | + This issue has been automatically marked as stale because it has been open 30 days + with no activity. Remove stale label or comment or this issue will be closed in 10 days + stale-pr-message: | + This PR has been automatically marked as stale because it has been open 30 days + with no activity. Remove stale label or comment or this PR will be closed in 10 days + # Not stale if have this labels + exempt-issue-labels: kind/bug,lifecycle/active,lifecycle/frozen + exempt-pr-labels: kind/bug,lifecycle/active,lifecycle/frozen + # If unstale + labels-to-remove-when-unstale: lifecycle/stale + # Close issue operations + # Label will be automatically removed if the issues are no longer closed nor locked. + days-before-close: 10 + close-issue-label: lifecycle/rotten + delete-branch: true + close-issue-message: This issue was automatically closed because of stale in 10 days + close-pr-message: This PR was automatically closed because of stale in 10 days diff --git a/Dockerfile b/Dockerfile index 5fa7552ac..0af14e304 100644 --- a/Dockerfile +++ b/Dockerfile @@ -123,18 +123,18 @@ RUN . /.env && \ ) && chmod +x tfsec \ ; fi -# Checking binaries versions +# Checking binaries versions and write it to debug file RUN . /.env && \ - echo "\n\n" && \ - pre-commit --version && \ - terraform --version | head -n 1 && \ - (if [ "$CHECKOV_VERSION" != "false" ]; then echo -n "checkov " && checkov --version; else echo "checkov SKIPPED" ; fi) && \ - (if [ "$TERRAFORM_DOCS_VERSION" != "false" ]; then ./terraform-docs --version; else echo "terraform-docs SKIPPED"; fi) && \ - (if [ "$TERRAGRUNT_VERSION" != "false" ]; then ./terragrunt --version; else echo "terragrunt SKIPPED" ; fi) && \ - (if [ "$TERRASCAN_VERSION" != "false" ]; then echo -n "terrascan " && ./terrascan version; else echo "terrascan SKIPPED" ; fi) && \ - (if [ "$TFLINT_VERSION" != "false" ]; then ./tflint --version; else echo "tflint SKIPPED" ; fi) && \ - (if [ "$TFSEC_VERSION" != "false" ]; then echo -n "tfsec " && ./tfsec --version; else echo "tfsec SKIPPED" ; fi) && \ - echo "\n\n" + F=tools_versions_info && \ + pre-commit --version >> $F && \ + terraform --version | head -n 1 >> $F && \ + (if [ "$CHECKOV_VERSION" != "false" ]; then echo "checkov $(checkov --version)" >> $F; else echo "checkov SKIPPED" >> $F ; fi) && \ + (if [ "$TERRAFORM_DOCS_VERSION" != "false" ]; then ./terraform-docs --version >> $F; else echo "terraform-docs SKIPPED" >> $F; fi) && \ + (if [ "$TERRAGRUNT_VERSION" != "false" ]; then ./terragrunt --version >> $F; else echo "terragrunt SKIPPED" >> $F ; fi) && \ + (if [ "$TERRASCAN_VERSION" != "false" ]; then echo "terrascan $(./terrascan version)" >> $F; else echo "terrascan SKIPPED" >> $F ; fi) && \ + (if [ "$TFLINT_VERSION" != "false" ]; then ./tflint --version >> $F; else echo "tflint SKIPPED" >> $F ; fi) && \ + (if [ "$TFSEC_VERSION" != "false" ]; then echo "tfsec $(./tfsec --version)" >> $F; else echo "tfsec SKIPPED" >> $F ; fi) && \ + echo "\n\n" && cat $F && echo "\n\n" # based on debian:buster-slim # https://github.com/docker-library/python/blob/master/3.9/buster/slim/Dockerfile diff --git a/README.md b/README.md index aeeb9668c..bd848d5fa 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@

* [`checkov`](https://github.com/bridgecrewio/checkov) required for `checkov` hook. * [`terraform-docs`](https://github.com/terraform-docs/terraform-docs) required for `terraform_docs` hooks. +* [`terragrunt`](https://terragrunt.gruntwork.io/docs/getting-started/install/) required for `terragrunt_validate` hook. * [`terrascan`](https://github.com/accurics/terrascan) required for `terrascan` hook. * [`TFLint`](https://github.com/terraform-linters/tflint) required for `terraform_tflint` hook. * [`TFSec`](https://github.com/liamg/tfsec) required for `terraform_tfsec` hook. @@ -163,6 +164,12 @@ Docker: docker run -v $(pwd):/lint -w /lint pre-commit run -a ``` +> You be able list tools versions when needed +> +> ```bash +> TAG=latest && docker run --entrypoint cat pre-commit:$TAG /usr/bin/tools_versions_info +> ``` + ## Available Hooks There are several [pre-commit](https://pre-commit.com/) hooks to keep Terraform configurations (both `*.tf` and `*.tfvars`) and Terragrunt configurations (`*.hcl`) in a good shape: From 7a8c6e109e1a321dfddfd9617bbe1c50bf440e6d Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Tue, 14 Sep 2021 13:37:22 +0200 Subject: [PATCH 128/214] chore: Updated GH stale action config (#223) --- .github/workflows/stale-actions.yaml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/.github/workflows/stale-actions.yaml b/.github/workflows/stale-actions.yaml index 0000f4075..f769925dc 100644 --- a/.github/workflows/stale-actions.yaml +++ b/.github/workflows/stale-actions.yaml @@ -12,8 +12,8 @@ jobs: repo-token: ${{ secrets.GITHUB_TOKEN }} # Staling issues and PR's days-before-stale: 30 - stale-issue-label: lifecycle/stale - stale-pr-label: lifecycle/stale + stale-issue-label: stale + stale-pr-label: stale stale-issue-message: | This issue has been automatically marked as stale because it has been open 30 days with no activity. Remove stale label or comment or this issue will be closed in 10 days @@ -21,14 +21,11 @@ jobs: This PR has been automatically marked as stale because it has been open 30 days with no activity. Remove stale label or comment or this PR will be closed in 10 days # Not stale if have this labels - exempt-issue-labels: kind/bug,lifecycle/active,lifecycle/frozen - exempt-pr-labels: kind/bug,lifecycle/active,lifecycle/frozen - # If unstale - labels-to-remove-when-unstale: lifecycle/stale + exempt-issue-labels: bug,wip,on-hold + exempt-pr-labels: bug,wip,on-hold # Close issue operations # Label will be automatically removed if the issues are no longer closed nor locked. days-before-close: 10 - close-issue-label: lifecycle/rotten delete-branch: true close-issue-message: This issue was automatically closed because of stale in 10 days close-pr-message: This PR was automatically closed because of stale in 10 days From 4faee7b12b741f9c58de438e082571ebe72883da Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Wed, 15 Sep 2021 13:57:58 +0200 Subject: [PATCH 129/214] fix: label auto-adding after label rename (#226) --- .github/ISSUE_TEMPLATE/bug_report_docker.md | 4 ++-- .github/ISSUE_TEMPLATE/bug_report_local_install.md | 4 ++-- .github/ISSUE_TEMPLATE/feature_request.md | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report_docker.md b/.github/ISSUE_TEMPLATE/bug_report_docker.md index f1cddc738..a47e30657 100644 --- a/.github/ISSUE_TEMPLATE/bug_report_docker.md +++ b/.github/ISSUE_TEMPLATE/bug_report_docker.md @@ -1,8 +1,8 @@ --- -name: Local installation bug report +name: Docker bug report about: Create a bug report labels: -- kind/bug +- bug - area/docker --- diff --git a/.github/ISSUE_TEMPLATE/bug_report_local_install.md b/.github/ISSUE_TEMPLATE/bug_report_local_install.md index f0798db1f..329a3ae88 100644 --- a/.github/ISSUE_TEMPLATE/bug_report_local_install.md +++ b/.github/ISSUE_TEMPLATE/bug_report_local_install.md @@ -1,8 +1,8 @@ --- -name: Docker bug report +name: Local installation bug report about: Create a bug report labels: -- kind/bug +- bug - area/local_installation --- diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 8d9f73189..d1b4b6424 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -2,7 +2,7 @@ name: Feature request about: Suggest an idea for this project labels: -- kind/feature +- feature --- +| Hook name | Description | Dependencies
[Install instructions here](#1-install-dependencies) | +| ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | +| `checkov` | [checkov](https://github.com/bridgecrewio/checkov) static analysis of terraform templates to spot potential security issues. [Hook notes](#checkov) | `checkov`
Ubuntu deps: `python3`, `python3-pip` | +| `terraform_docs_replace` | Runs `terraform-docs` and pipes the output directly to README.md | `terraform-docs` | +| `terraform_docs_without_`
`aggregate_type_defaults` | Inserts input and output documentation into `README.md` without aggregate type defaults. Hook notes same as for [terraform_docs](#terraform_docs) | `python3`, `terraform-docs` | +| `terraform_docs` | Inserts input and output documentation into `README.md`. Recommended. [Hook notes](#terraform_docs) | `terraform-docs` | +| `terraform_fmt` | Rewrites all Terraform configuration files to a canonical format. [Hook notes](#terraform_fmt) | - | +| `terraform_providers_lock` | Updates provider signatures in [dependency lock files](https://www.terraform.io/docs/cli/commands/providers/lock.html). [Hook notes](#terraform_providers_lock) | - | +| `terraform_tflint` | Validates all Terraform configuration files with [TFLint](https://github.com/terraform-linters/tflint). [Available TFLint rules](https://github.com/terraform-linters/tflint/tree/master/docs/rules#rules). [Hook notes](#terraform_tflint). | `tflint` | +| `terraform_tfsec` | [TFSec](https://github.com/liamg/tfsec) static analysis of terraform templates to spot potential security issues. [Hook notes](#terraform_tfsec) | `tfsec` | +| `terraform_validate` | Validates all Terraform configuration files. [Hook notes](#terraform_validate) | - | +| `terragrunt_fmt` | Rewrites all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) to a canonical format. | `terragrunt` | +| `terragrunt_validate` | Validates all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) | - | +| `terrascan` | [terrascan](https://github.com/accurics/terrascan) Detect compliance and security violations. | `terrascan` | + Check the [source file](https://github.com/antonbabenko/pre-commit-terraform/blob/master/.pre-commit-hooks.yaml) to know arguments used for each hook. @@ -221,9 +223,9 @@ For [checkov](https://github.com/bridgecrewio/checkov) you need to specify each if they are present in `README.md`. -2. It is possible to pass additional arguments to shell scripts when using `terraform_docs` and `terraform_docs_without_aggregate_type_defaults`. Send pull-request with the new hook if there is something missing. +2. It is possible to pass additional arguments to shell scripts when using `terraform_docs` and `terraform_docs_without_aggregate_type_defaults`. Send pull-request with the new hook if something is missing. -For these hooks you need to specify all arguments as one: +For these hooks, you need to specify all arguments as one: ```yaml - id: terraform_docs @@ -233,7 +235,7 @@ For these hooks you need to specify all arguments as one: ### terraform_docs_replace -`terraform_docs_replace` replaces the entire README.md rather than doing string replacement between markers. Put your additional documentation at the top of your `main.tf` for it to be pulled in. The optional `--dest` argument lets you change the name of the file that gets created/modified. +`terraform_docs_replace` replaces the entire README.md rather than doing string replacement between markers. Put your additional documentation at the top of your `main.tf` for it to be pulled in. The optional `--dest` argument lets you change the filename that gets created/modified. Example: @@ -274,7 +276,7 @@ Example: - '--args=-platform=darwin_amd64' ``` -4. It may happen that Terraform working directory (`.terraform`) already exists but not in the best condition (eg, not initialized modules, wrong version of Terraform, etc). To solve this problem you can find and delete all `.terraform` directories in your repository: +4. It may happen that Terraform working directory (`.terraform`) already exists but not in the best condition (eg, not initialized modules, wrong version of Terraform, etc.). To solve this problem, you can find and delete all `.terraform` directories in your repository: ```bash echo " @@ -283,14 +285,14 @@ Example: } " >>~/.bashrc - # Reload shell and use `rm_terraform` command in repo root + # Reload shell and use `rm_terraform` command in the repo root ``` - `terraform_providers_lock` hook will try to reinitialize them before running `terraform providers lock` command. + `terraform_providers_lock` hook will try to reinitialize them before running the `terraform providers lock` command. ### terraform_tflint -1. `terraform_tflint` supports custom arguments so you can enable module inspection, deep check mode etc. +1. `terraform_tflint` supports custom arguments so you can enable module inspection, deep check mode, etc. Example: @@ -301,7 +303,7 @@ Example: - --args=--enable-rule=terraform_documented_variables ``` -2. When you have multiple directories and want to run `tflint` in all of them and share single config file it is impractical to hard-code the path to `.tflint.hcl` file. The solution is to use `__GIT_WORKING_DIR__` placeholder which will be replaced by `terraform_tflint` hooks with Git working directory (repo root) at run time. For example: +2. When you have multiple directories and want to run `tflint` in all of them and share a single config file, it is impractical to hard-code the path to `.tflint.hcl` file. The solution is to use the `__GIT_WORKING_DIR__` placeholder which will be replaced by `terraform_tflint` hooks with Git working directory (repo root) at run time. For example: ```yaml - id: terraform_tflint @@ -373,7 +375,7 @@ Example: - --envs=AWS_SECRET_ACCESS_KEY="asecretkey" ``` -3. It may happen that Terraform working directory (`.terraform`) already exists but not in the best condition (eg, not initialized modules, wrong version of Terraform, etc). To solve this problem you can find and delete all `.terraform` directories in your repository: +3. It may happen that Terraform working directory (`.terraform`) already exists but not in the best condition (eg, not initialized modules, wrong version of Terraform, etc.). To solve this problem, you can find and delete all `.terraform` directories in your repository: ```bash echo " @@ -382,10 +384,10 @@ Example: } " >>~/.bashrc - # Reload shell and use `rm_terraform` command in repo root + # Reload shell and use `rm_terraform` command in the repo root ``` - `terraform_validate` hook will try to reinitialize them before running `terraform validate` command. + `terraform_validate` hook will try to reinitialize them before running the `terraform validate` command. **Warning:** If you use Terraform workspaces, DO NOT use this workaround ([details](https://github.com/antonbabenko/pre-commit-terraform/issues/203#issuecomment-918791847)). Wait to [`force-init`](https://github.com/antonbabenko/pre-commit-terraform/issues/224) option implementation @@ -394,10 +396,12 @@ Example: This repository is managed by [Anton Babenko](https://github.com/antonbabenko) with help from these awesome contributors: + + ## License -MIT licensed. See LICENSE for full details. +MIT licensed. See [LICENSE](LICENSE) for full details. From 25cddd988093033a6e2ee013571201e4bb89ca47 Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Fri, 15 Oct 2021 15:19:41 +0300 Subject: [PATCH 148/214] feat: Add support for specify terraform-docs config file (#244) --- terraform_docs.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/terraform_docs.sh b/terraform_docs.sh index 1eb3c0526..f4335cd34 100755 --- a/terraform_docs.sh +++ b/terraform_docs.sh @@ -4,6 +4,8 @@ set -eo pipefail main() { initialize_ parse_cmdline_ "$@" + # Support for setting relative PATH to .terraform-docs.yml config. + ARGS=${ARGS/--config=/--config=$(pwd)\/} terraform_docs_ "${ARGS[*]}" "${FILES[@]}" } From 3f6643280eb25021d8498e9abf930261377fe829 Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Fri, 15 Oct 2021 15:24:50 +0300 Subject: [PATCH 149/214] fix: terrafrom_tflint ERROR output for files located in repo root (#243) --- terraform_tflint.sh | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/terraform_tflint.sh b/terraform_tflint.sh index 6da3b93ac..0473a4a5f 100755 --- a/terraform_tflint.sh +++ b/terraform_tflint.sh @@ -61,19 +61,12 @@ tflint_() { for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do path_uniq="${path_uniq//__REPLACED__SPACE__/ }" - pushd "$path_uniq" > /dev/null - TFLINT_MSG=$( - tflint "${ARGS[@]}" 2>&1 || - echo >&2 -e "\033[1;31m\nERROR in ./$path_uniq/:\033[0m" && - tflint "${ARGS[@]}" # Print TFLint error with PATH - ) - # Print checked PATH if TFLint have any messages - if [ ! -z "$TFLINT_MSG" ]; then - echo -e "\n./$path_uniq/:" - echo "$TFLINT_MSG" - fi + # Print checked PATH **only** if TFLint have any messages + # shellcheck disable=SC2091 # Suppress error output + $(tflint "${ARGS[@]}" 2>&1) || + echo >&2 -e "\033[1;31m\nERROR in $path_uniq/:\033[0m" && tflint "${ARGS[@]}" popd > /dev/null done From 7b11401863b29fc8c83a1e74584ac99dc50630ec Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Fri, 15 Oct 2021 15:26:23 +0300 Subject: [PATCH 150/214] feat: Add `terraform_docs` hook settings (#245) --- README.md | 32 +++++++++++++---- terraform_docs.sh | 90 ++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 103 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 5ac14002d..51e0d319f 100644 --- a/README.md +++ b/README.md @@ -225,13 +225,33 @@ For [checkov](https://github.com/bridgecrewio/checkov) you need to specify each 2. It is possible to pass additional arguments to shell scripts when using `terraform_docs` and `terraform_docs_without_aggregate_type_defaults`. Send pull-request with the new hook if something is missing. -For these hooks, you need to specify all arguments as one: +3. It is possible to automatically: + * create docfile (and PATH to it) + * extend exiting docs files, by appending markers to the end of file (see p.1) + * use different than `README.md` docfile name. -```yaml -- id: terraform_docs - args: - - tfvars hcl --output-file terraform.tfvars.model . -``` + ```yaml + - id: terraform_docs + args: + - --hook-config=--path-to-file=README.md # Valid UNIX path. I.e. ../TFDOC.md or docs/README.md etc. + - --hook-config=--add-to-exiting-file=true # Boolean. true or false + - --hook-config=--create-file-if-not-exist=true # Boolean. true or false + ``` + +4. You can provide arguments to terraform_doc. Eg. for [configuration](https://github.com/terraform-docs/terraform-docs/blob/master/docs/user-guide/configuration.md#usage): + + ```yaml + - id: terraform_docs + args: + - --args=--config=.terraform-docs.yml + +5. If you need some exotic settings, it can be be done too. I.e. this one generates HCL files: + + ```yaml + - id: terraform_docs + args: + - tfvars hcl --output-file terraform.tfvars.model . + ``` ### terraform_docs_replace diff --git a/terraform_docs.sh b/terraform_docs.sh index f4335cd34..e817687c2 100755 --- a/terraform_docs.sh +++ b/terraform_docs.sh @@ -6,7 +6,7 @@ main() { parse_cmdline_ "$@" # Support for setting relative PATH to .terraform-docs.yml config. ARGS=${ARGS/--config=/--config=$(pwd)\/} - terraform_docs_ "${ARGS[*]}" "${FILES[@]}" + terraform_docs_ "${HOOK_CONFIG[*]}" "${ARGS[*]}" "${FILES[@]}" } initialize_() { @@ -29,7 +29,7 @@ initialize_() { parse_cmdline_() { declare argv - argv=$(getopt -o a: --long args: -- "$@") || return + argv=$(getopt -o a: --long args:,hook-config: -- "$@") || return eval "set -- $argv" for argv; do @@ -39,6 +39,11 @@ parse_cmdline_() { ARGS+=("$1") shift ;; + --hook-config) + shift + HOOK_CONFIG+=("$1") + shift + ;; --) shift FILES=("$@") @@ -49,8 +54,9 @@ parse_cmdline_() { } terraform_docs_() { - local -r args="$1" - shift + local -r hook_config="$1" + local -r args="$2" + shift 2 local -a -r files=("$@") local hack_terraform_docs @@ -66,7 +72,7 @@ terraform_docs_() { if [[ -z "$is_old_terraform_docs" ]]; then # Using terraform-docs 0.8+ (preferred) - terraform_docs "0" "$args" "${files[@]}" + terraform_docs "0" "$hook_config" "$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 @@ -78,20 +84,21 @@ terraform_docs_() { local tmp_file_awk tmp_file_awk=$(mktemp "${TMPDIR:-/tmp}/terraform-docs-XXXXXXXXXX") terraform_docs_awk "$tmp_file_awk" - terraform_docs "$tmp_file_awk" "$args" "${files[@]}" + terraform_docs "$tmp_file_awk" "$hook_config" "$args" "${files[@]}" rm -f "$tmp_file_awk" else # Using terraform 0.11 and no awk script is needed for that - terraform_docs "0" "$args" "${files[@]}" + terraform_docs "0" "$hook_config" "$args" "${files[@]}" fi } terraform_docs() { local -r terraform_docs_awk_file="$1" - local -r args="$2" - shift 2 + local -r hook_config="$2" + local -r args="$3" + shift 3 local -a -r files=("$@") declare -a paths @@ -107,7 +114,32 @@ terraform_docs() { done local -r tmp_file=$(mktemp) - local -r text_file="README.md" + + # + # Get hook settings + # + local text_file="README.md" + local add_to_exiting=false + local create_if_not_exist=false + + configs=($hook_config) + for c in "${configs[@]}"; do + config=(${c//=/ }) + key=${config[0]} + value=${config[1]} + + case $key in + --path-to-file) + text_file=$value + ;; + --add-to-exiting-file) + add_to_exiting=$value + ;; + --create-file-if-not-exist) + create_if_not_exist=$value + ;; + esac + done local path_uniq for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do @@ -115,9 +147,40 @@ terraform_docs() { pushd "$path_uniq" > /dev/null - if [[ ! -f "$text_file" ]]; then - popd > /dev/null - continue + # + # Create file if it not exist and `--create-if-not-exist=true` provided + # + if $create_if_not_exist && [[ ! -f "$text_file" ]]; then + dir_have_tf_files="$( + find . -maxdepth 1 -type f | sed 's|.*\.||' | sort -u | grep -oE '^tf$|^tfvars$' || + exit 0 + )" + + # if no TF files - skip dir + [ ! "$dir_have_tf_files" ] && popd > /dev/null && continue + + dir="$(dirname "$text_file")" + + mkdir -p "$dir" + echo -e "# ${PWD##*/}\n" >> "$text_file" + echo "" >> "$text_file" + echo "" >> "$text_file" + fi + + # If file still not exist - skip dir + [[ ! -f "$text_file" ]] && popd > /dev/null && continue + + # + # If `--add-to-exiting-file=true` set, check is in file exist "hook markers", + # and if not - append "hook markers" to the end of file. + # + if $add_to_exiting; then + HAVE_MARKER=$(grep -o '' "$text_file" || exit 0) + + if [ ! "$HAVE_MARKER" ]; then + echo "" >> "$text_file" + echo "" >> "$text_file" + fi fi if [[ "$terraform_docs_awk_file" == "0" ]]; then @@ -311,5 +374,6 @@ EOF # global arrays declare -a ARGS=() declare -a FILES=() +declare -a HOOK_CONFIG=() [[ ${BASH_SOURCE[0]} != "$0" ]] || main "$@" From 762f7b252539cbf31d43bd4f89e1eb969f3c8988 Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Fri, 15 Oct 2021 15:37:49 +0300 Subject: [PATCH 151/214] docs: fix deps (#249) --- README.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 51e0d319f..cad9d19a7 100644 --- a/README.md +++ b/README.md @@ -180,19 +180,19 @@ There are several [pre-commit](https://pre-commit.com/) hooks to keep Terraform | Hook name | Description | Dependencies
[Install instructions here](#1-install-dependencies) | -| ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | -| `checkov` | [checkov](https://github.com/bridgecrewio/checkov) static analysis of terraform templates to spot potential security issues. [Hook notes](#checkov) | `checkov`
Ubuntu deps: `python3`, `python3-pip` | -| `terraform_docs_replace` | Runs `terraform-docs` and pipes the output directly to README.md | `terraform-docs` | -| `terraform_docs_without_`
`aggregate_type_defaults` | Inserts input and output documentation into `README.md` without aggregate type defaults. Hook notes same as for [terraform_docs](#terraform_docs) | `python3`, `terraform-docs` | -| `terraform_docs` | Inserts input and output documentation into `README.md`. Recommended. [Hook notes](#terraform_docs) | `terraform-docs` | -| `terraform_fmt` | Rewrites all Terraform configuration files to a canonical format. [Hook notes](#terraform_fmt) | - | -| `terraform_providers_lock` | Updates provider signatures in [dependency lock files](https://www.terraform.io/docs/cli/commands/providers/lock.html). [Hook notes](#terraform_providers_lock) | - | -| `terraform_tflint` | Validates all Terraform configuration files with [TFLint](https://github.com/terraform-linters/tflint). [Available TFLint rules](https://github.com/terraform-linters/tflint/tree/master/docs/rules#rules). [Hook notes](#terraform_tflint). | `tflint` | -| `terraform_tfsec` | [TFSec](https://github.com/liamg/tfsec) static analysis of terraform templates to spot potential security issues. [Hook notes](#terraform_tfsec) | `tfsec` | -| `terraform_validate` | Validates all Terraform configuration files. [Hook notes](#terraform_validate) | - | -| `terragrunt_fmt` | Rewrites all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) to a canonical format. | `terragrunt` | -| `terragrunt_validate` | Validates all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) | - | -| `terrascan` | [terrascan](https://github.com/accurics/terrascan) Detect compliance and security violations. | `terrascan` | +| ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | +| `checkov` | [checkov](https://github.com/bridgecrewio/checkov) static analysis of terraform templates to spot potential security issues. [Hook notes](#checkov) | `checkov`
Ubuntu deps: `python3`, `python3-pip` | +| `terraform_docs_replace` | Runs `terraform-docs` and pipes the output directly to README.md | `python3`, `terraform-docs` | +| `terraform_docs_without_`
`aggregate_type_defaults` | Inserts input and output documentation into `README.md` without aggregate type defaults. Hook notes same as for [terraform_docs](#terraform_docs) | `terraform-docs` | +| `terraform_docs` | Inserts input and output documentation into `README.md`. Recommended. [Hook notes](#terraform_docs) | `terraform-docs` | +| `terraform_fmt` | Rewrites all Terraform configuration files to a canonical format. [Hook notes](#terraform_fmt) | - | +| `terraform_providers_lock` | Updates provider signatures in [dependency lock files](https://www.terraform.io/docs/cli/commands/providers/lock.html). [Hook notes](#terraform_providers_lock) | - | +| `terraform_tflint` | Validates all Terraform configuration files with [TFLint](https://github.com/terraform-linters/tflint). [Available TFLint rules](https://github.com/terraform-linters/tflint/tree/master/docs/rules#rules). [Hook notes](#terraform_tflint). | `tflint` | +| `terraform_tfsec` | [TFSec](https://github.com/liamg/tfsec) static analysis of terraform templates to spot potential security issues. [Hook notes](#terraform_tfsec) | `tfsec` | +| `terraform_validate` | Validates all Terraform configuration files. [Hook notes](#terraform_validate) | - | +| `terragrunt_fmt` | Rewrites all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) to a canonical format. | `terragrunt` | +| `terragrunt_validate` | Validates all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) | `terragrunt` | +| `terrascan` | [terrascan](https://github.com/accurics/terrascan) Detect compliance and security violations. | `terrascan` | Check the [source file](https://github.com/antonbabenko/pre-commit-terraform/blob/master/.pre-commit-hooks.yaml) to know arguments used for each hook. From 390a2645f281163a04fea78e219eceb00c48addd Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Fri, 15 Oct 2021 22:11:01 +0300 Subject: [PATCH 152/214] fix: execute tflint once in no errors (#250) --- terraform_tflint.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/terraform_tflint.sh b/terraform_tflint.sh index 0473a4a5f..fbb7bb6a8 100755 --- a/terraform_tflint.sh +++ b/terraform_tflint.sh @@ -65,8 +65,10 @@ tflint_() { # Print checked PATH **only** if TFLint have any messages # shellcheck disable=SC2091 # Suppress error output - $(tflint "${ARGS[@]}" 2>&1) || - echo >&2 -e "\033[1;31m\nERROR in $path_uniq/:\033[0m" && tflint "${ARGS[@]}" + $(tflint "${ARGS[@]}" 2>&1) || { + echo >&2 -e "\033[1;31m\nERROR in $path_uniq/:\033[0m" + tflint "${ARGS[@]}" + } popd > /dev/null done From e33c654a4005cff364a42f965068160e4ba3e3c2 Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Fri, 15 Oct 2021 23:09:13 +0300 Subject: [PATCH 153/214] fix: command not found (#251) --- terraform_tflint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform_tflint.sh b/terraform_tflint.sh index fbb7bb6a8..52c57566e 100755 --- a/terraform_tflint.sh +++ b/terraform_tflint.sh @@ -65,7 +65,7 @@ tflint_() { # Print checked PATH **only** if TFLint have any messages # shellcheck disable=SC2091 # Suppress error output - $(tflint "${ARGS[@]}" 2>&1) || { + $(tflint "${ARGS[@]}" 2>&1) 2> /dev/null || { echo >&2 -e "\033[1;31m\nERROR in $path_uniq/:\033[0m" tflint "${ARGS[@]}" } From c0b9b3c5622f09123d21570807b0005c57bcb2b5 Mon Sep 17 00:00:00 2001 From: Dan Arnold Date: Tue, 19 Oct 2021 03:29:27 -0700 Subject: [PATCH 154/214] docs: Add missing space in terrascan install cmd (#253) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cad9d19a7..bead61f68 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ python3.7 -m pip install -U checkov curl -L "$(curl -s https://api.github.com/repos/terraform-docs/terraform-docs/releases/latest | grep -o -E -m 1 "https://.+?-linux-amd64.tar.gz")" > terraform-docs.tgz && tar -xzf terraform-docs.tgz && rm terraform-docs.tgz && chmod +x terraform-docs && sudo mv terraform-docs /usr/bin/ curl -L "$(curl -s https://api.github.com/repos/terraform-linters/tflint/releases/latest | grep -o -E -m 1 "https://.+?_linux_amd64.zip")" > tflint.zip && unzip tflint.zip && rm tflint.zip && sudo mv tflint /usr/bin/ curl -L "$(curl -s https://api.github.com/repos/aquasecurity/tfsec/releases/latest | grep -o -E -m 1 "https://.+?tfsec-linux-amd64")" > tfsec && chmod +x tfsec && sudo mv tfsec /usr/bin/ -curl -L "$(curl -s https://api.github.com/repos/accurics/terrascan/releases/latest | grep -o -E -m 1"https://.+?_Linux_x86_64.tar.gz")" > terrascan.tar.gz && tar -xzf terrascan.tar.gz terrascan && rm terrascan.tar.gz && sudo mv terrascan /usr/bin/ && terrascan init +curl -L "$(curl -s https://api.github.com/repos/accurics/terrascan/releases/latest | grep -o -E -m 1 "https://.+?_Linux_x86_64.tar.gz")" > terrascan.tar.gz && tar -xzf terrascan.tar.gz terrascan && rm terrascan.tar.gz && sudo mv terrascan /usr/bin/ && terrascan init ``` @@ -117,7 +117,7 @@ python3 -m pip install --upgrade pip pip3 install --no-cache-dir pre-commit pip3 install --no-cache-dir checkov curl -L "$(curl -s https://api.github.com/repos/terraform-docs/terraform-docs/releases/latest | grep -o -E -m 1 "https://.+?-linux-amd64.tar.gz")" > terraform-docs.tgz && tar -xzf terraform-docs.tgz terraform-docs && rm terraform-docs.tgz && chmod +x terraform-docs && sudo mv terraform-docs /usr/bin/ -curl -L "$(curl -s https://api.github.com/repos/accurics/terrascan/releases/latest | grep -o -E -m 1"https://.+?_Linux_x86_64.tar.gz")" > terrascan.tar.gz && tar -xzf terrascan.tar.gz terrascan && rm terrascan.tar.gz && sudo mv terrascan /usr/bin/ && terrascan init +curl -L "$(curl -s https://api.github.com/repos/accurics/terrascan/releases/latest | grep -o -E -m 1 "https://.+?_Linux_x86_64.tar.gz")" > terrascan.tar.gz && tar -xzf terrascan.tar.gz terrascan && rm terrascan.tar.gz && sudo mv terrascan /usr/bin/ && terrascan init curl -L "$(curl -s https://api.github.com/repos/terraform-linters/tflint/releases/latest | grep -o -E -m 1 "https://.+?_linux_amd64.zip")" > tflint.zip && unzip tflint.zip && rm tflint.zip && sudo mv tflint /usr/bin/ curl -L "$(curl -s https://api.github.com/repos/aquasecurity/tfsec/releases/latest | grep -o -E -m 1 "https://.+?tfsec-linux-amd64")" > tfsec && chmod +x tfsec && sudo mv tfsec /usr/bin/ ``` From 2973f85f3ba5f8f5b4610cce9b01a7ce26510c84 Mon Sep 17 00:00:00 2001 From: gravitybacklight <2327204+gravitybacklight@users.noreply.github.com> Date: Thu, 21 Oct 2021 15:13:34 +0100 Subject: [PATCH 155/214] feat: add __GIT_WORKING_DIR__ to tfsec (#255) --- README.md | 7 +++++++ terraform_tfsec.sh | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index bead61f68..637d66b82 100644 --- a/README.md +++ b/README.md @@ -372,7 +372,14 @@ Example: --no-color -e aws-s3-enable-bucket-logging,aws-s3-specify-public-access-block ``` +4. Like terraform_tflint, `__GIT_WORKING_DIR__` can be used when specifying files relative to the git working directory: +Example: + + ```yaml + - id: terraform_tfsec + args: [--args=--config-file=__GIT_WORKING_DIR__/.tfsec.json] + ``` ### terraform_validate diff --git a/terraform_tfsec.sh b/terraform_tfsec.sh index c742f3215..0dc01fe10 100755 --- a/terraform_tfsec.sh +++ b/terraform_tfsec.sh @@ -54,7 +54,8 @@ parse_cmdline_() { case $argv in -a | --args) shift - ARGS+=("$1") + expanded_arg="${1//__GIT_WORKING_DIR__/$PWD}" + ARGS+=("$expanded_arg") shift ;; --) From a0f69e3ba784762285ad3e097bb33c463340f042 Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Mon, 25 Oct 2021 21:10:36 +0300 Subject: [PATCH 156/214] docs: fix protocol to prevent MITM (#257) --- .pre-commit-config.yaml | 4 ++-- README.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 020f65f4c..f1a7a65ef 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,5 @@ repos: -- repo: git://github.com/pre-commit/pre-commit-hooks +- repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.0.1 hooks: # Git style @@ -28,7 +28,7 @@ repos: - id: detect-private-key -- repo: git://github.com/jumanjihouse/pre-commit-hooks +- repo: https://github.com/jumanjihouse/pre-commit-hooks rev: 2.1.5 hooks: - id: shfmt diff --git a/README.md b/README.md index 637d66b82..a6d8ccaba 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,7 @@ Step into the repository you want to have the pre-commit hooks installed and run git init cat < .pre-commit-config.yaml repos: -- repo: git://github.com/antonbabenko/pre-commit-terraform +- repo: https://github.com/antonbabenko/pre-commit-terraform rev: # Get the latest from: https://github.com/antonbabenko/pre-commit-terraform/releases hooks: - id: terraform_fmt From cc59119c174106747453473c4a6e3d775c555c60 Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Mon, 25 Oct 2021 21:31:55 +0300 Subject: [PATCH 157/214] feat: Set up PR reviewers automatically (#258) --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..e8de3562f --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @maxymvlasov @yermulnik From cff42e6d6fe8cc33f4a7033e7f945e0668c32be8 Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Tue, 26 Oct 2021 14:12:01 +0300 Subject: [PATCH 158/214] feat: Add infracost_breakdown hook (#252) --- .github/CONTRIBUTING.md | 51 ++++++++++++- .pre-commit-hooks.yaml | 9 +++ Dockerfile | 21 +++++- README.md | 106 +++++++++++++++++++++++++- infracost_breakdown.sh | 163 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 344 insertions(+), 6 deletions(-) create mode 100755 infracost_breakdown.sh diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 28233fef5..dc3191a40 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -12,6 +12,11 @@ Enjoy the clean, valid, and documented code! * [Run via Docker](#run-via-docker) * [Check results](#check-results) * [Cleanup](#cleanup) +* [Add new hook](#add-new-hook) + * [Before write code](#before-write-code) + * [Prepare basic documentation](#prepare-basic-documentation) + * [Add code](#add-code) + * [Finish with the documentation](#finish-with-the-documentation) ## Run and debug hooks locally @@ -41,7 +46,7 @@ For example, to test that the [`terraform_fmt`](../README.md#terraform_fmt) hook To check is your improvement not violate performance, we have dummy execution time tests. Script accept next options: - + | # | Name | Example value | Description | | --- | ---------------------------------- | ------------------------------------------------------------------------ | ---------------------------------------------------- | | 1 | `TEST_NUM` | `200` | How many times need repeat test | @@ -49,6 +54,7 @@ Script accept next options: | 3 | `TEST_DIR` | `'/tmp/infrastructure'` | Dir on what you run tests. | | 4 | `TEST_DESCRIPTION` | ```'`terraform_tfsec` PR #123:'``` | Text that you'd like to see in result | | 5 | `RAW_TEST_`
`RESULTS_FILE_NAME` | `terraform_tfsec_pr123` | (Temporary) File where all test data will be stored. | + ### Run via BASH @@ -87,3 +93,46 @@ Results will be located at `./test/results` dir. ```bash sudo rm -rf tests/results ``` + +## Add new hook + +You can use [this PR](https://github.com/antonbabenko/pre-commit-terraform/pull/252) as an example. + +### Before write code + +1. Try to figure out future hook usage. +2. Confirm the concept with [Anton Babenko](https://github.com/antonbabenko). + +### Prepare basic documentation + +1. Identify and describe dependencies in [Install dependencies](../README.md#1-install-dependencies) and [Available Hooks](../README.md#available-hooks) sections + +### Add code + +1. Based on prev. block, add hook dependencies installation to [Dockerfile](../Dockerfile). + Check that works: + * `docker build -t pre-commit --build-arg INSTALL_ALL=true .` + * `docker build -t pre-commit --build-arg _VERSION=latest .` + * `docker build -t pre-commit --build-arg _VERSION=<1.2.3> .` +2. Add new hook to [`.pre-commit-hooks.yaml`](../.pre-commit-hooks.yaml) +3. Create hook file. Don't forget to make it executable via `chmod +x /path/to/hook/file`. +4. Test hook. How to do it is described in [Run and debug hooks locally](#run-and-debug-hooks-locally) section. +5. Test hook one more time. + 1. Push commit with hook file to GitHub + 2. Grab SHA hash of the commit + 3. Test hook using `.pre-commit-config.yaml`: + + ```yaml + repos: + - repo: https://github.com/antonbabenko/pre-commit-terraform # Your repo + rev: 3d76da3885e6a33d59527eff3a57d246dfb66620 # Your commit SHA + hooks: + - id: terraform_docs # New hook name + args: + - --args=--config=.terraform-docs.yml # Some args that you'd like to test + ``` + +### Finish with the documentation + +1. Add hook description to [Available Hooks](../README.md#available-hooks). +2. Create and populate a new hook section in [Hooks usage notes and examples](../README.md#hooks-usage-notes-and-examples). diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 492b559a5..08e65ba27 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -1,3 +1,12 @@ +- id: infracost_breakdown + name: Infracost breakdown + description: Check terraform infrastructure cost + entry: infracost_breakdown.sh + language: script + require_serial: true + files: \.(tf(vars)?|hcl)$ + exclude: \.terraform\/.*$ + - id: terraform_fmt name: Terraform fmt description: Rewrites all Terraform configuration files to a canonical format. diff --git a/Dockerfile b/Dockerfile index 18e1c7c63..39a539cd2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,9 @@ RUN apt update && \ software-properties-common \ curl \ python3 \ - python3-pip && \ + python3-pip \ + # infracost deps + jq && \ # Upgrade pip for be able get latest Checkov python3 -m pip install --upgrade pip && \ # Cleanup @@ -41,6 +43,7 @@ RUN curl -fsSL https://apt.releases.hashicorp.com/gpg | apt-key add - && \ WORKDIR /bin_dir ARG CHECKOV_VERSION=${CHECKOV_VERSION:-false} +ARG INFRACOST_VERSION=${INFRACOST_VERSION:-false} ARG TERRAFORM_DOCS_VERSION=${TERRAFORM_DOCS_VERSION:-false} ARG TERRAGRUNT_VERSION=${TERRAGRUNT_VERSION:-false} ARG TERRASCAN_VERSION=${TERRASCAN_VERSION:-false} @@ -54,6 +57,7 @@ ARG TFSEC_VERSION=${TFSEC_VERSION:-false} ARG INSTALL_ALL=${INSTALL_ALL:-false} RUN if [ "$INSTALL_ALL" != "false" ]; then \ echo "export CHECKOV_VERSION=latest" >> /.env && \ + echo "export INFRACOST_VERSION=latest" >> /.env && \ echo "export TERRAFORM_DOCS_VERSION=latest" >> /.env && \ echo "export TERRAGRUNT_VERSION=latest" >> /.env && \ echo "export TERRASCAN_VERSION=latest" >> /.env && \ @@ -73,6 +77,16 @@ RUN . /.env && \ ) \ ; fi +# infracost +RUN . /.env && \ + if [ "$INFRACOST_VERSION" != "false" ]; then \ + ( \ + INFRACOST_RELEASES="https://api.github.com/repos/infracost/infracost/releases" && \ + [ "$INFRACOST_VERSION" = "latest" ] && curl -L "$(curl -s ${INFRACOST_RELEASES}/latest | grep -o -E -m 1 "https://.+?-linux-amd64.tar.gz")" > infracost.tgz \ + || curl -L "$(curl -s ${INFRACOST_RELEASES} | grep -o -E "https://.+?v${INFRACOST_VERSION}/infracost-linux-amd64.tar.gz")" > infracost.tgz \ + ) && tar -xzf infracost.tgz && rm infracost.tgz && mv infracost-linux-amd64 infracost \ + ; fi + # Terraform docs RUN . /.env && \ if [ "$TERRAFORM_DOCS_VERSION" != "false" ]; then \ @@ -131,6 +145,7 @@ RUN . /.env && \ pre-commit --version >> $F && \ terraform --version | head -n 1 >> $F && \ (if [ "$CHECKOV_VERSION" != "false" ]; then echo "checkov $(checkov --version)" >> $F; else echo "checkov SKIPPED" >> $F ; fi) && \ + (if [ "$INFRACOST_VERSION" != "false" ]; then echo "$(./infracost --version)" >> $F; else echo "infracost SKIPPED" >> $F ; fi) && \ (if [ "$TERRAFORM_DOCS_VERSION" != "false" ]; then ./terraform-docs --version >> $F; else echo "terraform-docs SKIPPED" >> $F; fi) && \ (if [ "$TERRAGRUNT_VERSION" != "false" ]; then ./terragrunt --version >> $F; else echo "terragrunt SKIPPED" >> $F ; fi) && \ (if [ "$TERRASCAN_VERSION" != "false" ]; then echo "terrascan $(./terrascan version)" >> $F; else echo "terrascan SKIPPED" >> $F ; fi) && \ @@ -159,10 +174,14 @@ COPY --from=builder \ /usr/local/bin/pre-commit \ /usr/bin/git \ /usr/bin/git-shell \ + /usr/bin/jq \ /usr/bin/ # Copy terrascan policies COPY --from=builder /root/ /root/ ENV PRE_COMMIT_COLOR=${PRE_COMMIT_COLOR:-always} +ENV INFRACOST_API_KEY=${INFRACOST_API_KEY:-} +ENV INFRACOST_SKIP_UPDATE_CHECK=${INFRACOST_SKIP_UPDATE_CHECK:-false} + ENTRYPOINT [ "pre-commit" ] diff --git a/README.md b/README.md index a6d8ccaba..46ace4701 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Want to Contribute? Check [open issues](https://github.com/antonbabenko/pre-comm * [Available Hooks](#available-hooks) * [Hooks usage notes and examples](#hooks-usage-notes-and-examples) * [checkov](#checkov) + * [infracost_breakdown](#infracost_breakdown) * [terraform_docs](#terraform_docs) * [terraform_docs_replace](#terraform_docs_replace) * [terraform_fmt](#terraform_fmt) @@ -45,6 +46,8 @@ Want to Contribute? Check [open issues](https://github.com/antonbabenko/pre-comm * [`terrascan`](https://github.com/accurics/terrascan) required for `terrascan` hook. * [`TFLint`](https://github.com/terraform-linters/tflint) required for `terraform_tflint` hook. * [`TFSec`](https://github.com/liamg/tfsec) required for `terraform_tfsec` hook. +* [`infracost`](https://github.com/infracost/infracost) required for `infracost_breakdown` hook. +* [`jq`](https://github.com/stedolan/jq) required for `infracost_breakdown` hook.
Docker
@@ -65,6 +68,7 @@ docker build -t pre-commit \ --build-arg PRE_COMMIT_VERSION=latest \ --build-arg TERRAFORM_VERSION=latest \ --build-arg CHECKOV_VERSION=2.0.405 \ + --build-arg INFRACOST_VERSION=latest \ --build-arg TERRAFORM_DOCS_VERSION=0.15.0 \ --build-arg TERRAGRUNT_VERSION=latest \ --build-arg TERRASCAN_VERSION=1.10.0 \ @@ -83,8 +87,9 @@ To disable the pre-commit color output, set `-e PRE_COMMIT_COLOR=never`. [`coreutils`](https://formulae.brew.sh/formula/coreutils) required for `terraform_validate` hook on macOS (due to use of `realpath`). ```bash -brew install pre-commit terraform-docs tflint tfsec coreutils checkov terrascan +brew install pre-commit terraform-docs tflint tfsec coreutils checkov terrascan infracost jq terrascan init +infracost register ```
@@ -103,6 +108,8 @@ curl -L "$(curl -s https://api.github.com/repos/terraform-docs/terraform-docs/re curl -L "$(curl -s https://api.github.com/repos/terraform-linters/tflint/releases/latest | grep -o -E -m 1 "https://.+?_linux_amd64.zip")" > tflint.zip && unzip tflint.zip && rm tflint.zip && sudo mv tflint /usr/bin/ curl -L "$(curl -s https://api.github.com/repos/aquasecurity/tfsec/releases/latest | grep -o -E -m 1 "https://.+?tfsec-linux-amd64")" > tfsec && chmod +x tfsec && sudo mv tfsec /usr/bin/ curl -L "$(curl -s https://api.github.com/repos/accurics/terrascan/releases/latest | grep -o -E -m 1 "https://.+?_Linux_x86_64.tar.gz")" > terrascan.tar.gz && tar -xzf terrascan.tar.gz terrascan && rm terrascan.tar.gz && sudo mv terrascan /usr/bin/ && terrascan init +sudo apt install -y jq && \ +curl -L "$(curl -s https://api.github.com/repos/infracost/infracost/releases/latest | grep -o -E -m 1 "https://.+?-linux-amd64.tar.gz")" > infracost.tgz && tar -xzf infracost.tgz && rm infracost.tgz && sudo mv infracost-linux-amd64 /usr/bin/infracost && infracost register ``` @@ -120,6 +127,8 @@ curl -L "$(curl -s https://api.github.com/repos/terraform-docs/terraform-docs/re curl -L "$(curl -s https://api.github.com/repos/accurics/terrascan/releases/latest | grep -o -E -m 1 "https://.+?_Linux_x86_64.tar.gz")" > terrascan.tar.gz && tar -xzf terrascan.tar.gz terrascan && rm terrascan.tar.gz && sudo mv terrascan /usr/bin/ && terrascan init curl -L "$(curl -s https://api.github.com/repos/terraform-linters/tflint/releases/latest | grep -o -E -m 1 "https://.+?_linux_amd64.zip")" > tflint.zip && unzip tflint.zip && rm tflint.zip && sudo mv tflint /usr/bin/ curl -L "$(curl -s https://api.github.com/repos/aquasecurity/tfsec/releases/latest | grep -o -E -m 1 "https://.+?tfsec-linux-amd64")" > tfsec && chmod +x tfsec && sudo mv tfsec /usr/bin/ +sudo apt install -y jq && \ +curl -L "$(curl -s https://api.github.com/repos/infracost/infracost/releases/latest | grep -o -E -m 1 "https://.+?-linux-amd64.tar.gz")" > infracost.tgz && tar -xzf infracost.tgz && rm infracost.tgz && sudo mv infracost-linux-amd64 /usr/bin/infracost && infracost register ``` @@ -182,6 +191,7 @@ There are several [pre-commit](https://pre-commit.com/) hooks to keep Terraform | Hook name | Description | Dependencies
[Install instructions here](#1-install-dependencies) | | ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | | `checkov` | [checkov](https://github.com/bridgecrewio/checkov) static analysis of terraform templates to spot potential security issues. [Hook notes](#checkov) | `checkov`
Ubuntu deps: `python3`, `python3-pip` | +| `infracost_breakdown` | Check how much your infra costs with [infracost](https://github.com/infracost/infracost). [Hook notes](#infracost_breakdown) | `infracost`, `jq`, [Infracost API key](https://www.infracost.io/docs/#2-get-api-key), Internet connection | `terraform_docs_replace` | Runs `terraform-docs` and pipes the output directly to README.md | `python3`, `terraform-docs` | | `terraform_docs_without_`
`aggregate_type_defaults` | Inserts input and output documentation into `README.md` without aggregate type defaults. Hook notes same as for [terraform_docs](#terraform_docs) | `terraform-docs` | | `terraform_docs` | Inserts input and output documentation into `README.md`. Recommended. [Hook notes](#terraform_docs) | `terraform-docs` | @@ -211,6 +221,94 @@ For [checkov](https://github.com/bridgecrewio/checkov) you need to specify each ] ``` +### infracost_breakdown + +`infracost_breakdown` build on top of the `infracost breakdown` command. It, if needed, runs `terraform init`, `terraform plan` and calls `infracost` API - so this hook can run up to several minutes. + +Unlike most other hooks, this one triggers all changes to the files but checks predefined paths each time. + +For example, the hook tracks `--path=./env/dev` and `./env/dev` depend on `./main.tf`. So when you will make changes to `./main.tf` - the hook will run and show the cost changes for `./env/dev`. + + +1. `infracost_breakdown` supports custom arguments so you can pass [supported flags](https://www.infracost.io/docs/#useful-options). + The following example only shows costs: + + ```yaml + - id: infracost_breakdown + args: + - --args=--path=./env/dev + verbose: true # Always show costs + ``` + +
Output + + ```bash + Running in "env/dev" + + Summary: { + "unsupportedResourceCounts": { + "aws_sns_topic_subscription": 1 + } + } + + Total Monthly Cost: 86.83 USD + Total Monthly Cost (diff): 86.83 USD + ``` + +
+ +2. You can provide limitations when the hook should fail: + + ```yaml + - id: infracost_breakdown + args: + - --args=--path=./env/dev + - --hook-config=.totalHourlyCost|tonumber > 0.1 + - --hook-config=.totalHourlyCost|tonumber > 1 + - --hook-config=.projects[].diff.totalMonthlyCost|tonumber != 10000 + - --hook-config=.currency == "USD" + ``` + +
Output + + ```bash + Running in "env/dev" + Passed: .totalHourlyCost|tonumber > 0.1 0.11894520547945205 > 0.1 + Failed: .totalHourlyCost|tonumber > 1 0.11894520547945205 > 1 + Passed: .projects[].diff.totalMonthlyCost|tonumber !=10000 86.83 != 10000 + Passed: .currency == "USD" "USD" == "USD" + + Summary: { + "unsupportedResourceCounts": { + "aws_sns_topic_subscription": 1 + } + } + + Total Monthly Cost: 86.83 USD + Total Monthly Cost (diff): 86.83 USD + ``` + +
+ + * Hook uses `jq` to parse `infracost` output, so paths to values like `.totalHourlyCost` and `.totalMonthlyCost` should be in jq-compatible format. + To check available structure use `infracost breakdown -p PATH_TO_TF_DIR --format json | jq -r . > infracost.json`. And play with it on [jqplay.org](https://jqplay.org/) + * Supported comparison operators: `<`, `<=`, `==`, `!=`, `>=`, `>`. + * Most useful paths and checks: + * `.totalHourlyCost` (same to `.projects[].breakdown.totalHourlyCost`) - show total hourly infra cost + * `.totalMonthlyCost` (same to `.projects[].breakdown.totalMonthlyCost`) - show total monthly infra cost + * `.projects[].diff.totalHourlyCost` - show hourly cost diff between existing infra and tf plan + * `.projects[].diff.totalMonthlyCost` - show monthly cost diff between existing infra and tf plan + * `.diffTotalHourlyCost` (for Infracost version 0.9.12 or newer) or `[.projects[].diff.totalMonthlyCost | select (.!=null) | tonumber] | add > 1000` (for Infracost older than 0.9.12): + * fail if changes push the total monthly cost estimate above $1K + * fail if changes increase the cost by $1K. + * You can set up only one path per one hook (`- id: infracost_breakdown`) - this is an `infracost` limitation. + * Set `verbose: true` to see cost even when the checks are passed. + * To disable hook color output, set `PRE_COMMIT_COLOR=never` env var + +3. **Docker usage**. In `docker build` or `docker run` command: + * You need to provide [Infracost API key](https://www.infracost.io/docs/integrations/environment_variables/#infracost_api_key) via `-e INFRACOST_API_KEY=`. By default it is saved in `~/.config/infracost/credentials.yml` + * Set `-e INFRACOST_SKIP_UPDATE_CHECK=true` to skip the Infracost update check; can be useful in CI/CD systems. [Doc](https://www.infracost.io/docs/integrations/environment_variables/#infracost_skip_update_check) + ### terraform_docs 1. `terraform_docs` and `terraform_docs_without_aggregate_type_defaults` will insert/update documentation generated by [terraform-docs](https://github.com/terraform-docs/terraform-docs) framed by markers: @@ -227,7 +325,7 @@ For [checkov](https://github.com/bridgecrewio/checkov) you need to specify each 3. It is possible to automatically: * create docfile (and PATH to it) - * extend exiting docs files, by appending markers to the end of file (see p.1) + * extend existing doc files by appending markers to the end of the file (see item 1) * use different than `README.md` docfile name. ```yaml @@ -245,7 +343,7 @@ For [checkov](https://github.com/bridgecrewio/checkov) you need to specify each args: - --args=--config=.terraform-docs.yml -5. If you need some exotic settings, it can be be done too. I.e. this one generates HCL files: +5. If you need some exotic settings, it can be done too. I.e. this one generates HCL files: ```yaml - id: terraform_docs @@ -323,7 +421,7 @@ Example: - --args=--enable-rule=terraform_documented_variables ``` -2. When you have multiple directories and want to run `tflint` in all of them and share a single config file, it is impractical to hard-code the path to `.tflint.hcl` file. The solution is to use the `__GIT_WORKING_DIR__` placeholder which will be replaced by `terraform_tflint` hooks with Git working directory (repo root) at run time. For example: +2. When you have multiple directories and want to run `tflint` in all of them and share a single config file, it is impractical to hard-code the path to the `.tflint.hcl` file. The solution is to use the `__GIT_WORKING_DIR__` placeholder which will be replaced by `terraform_tflint` hooks with Git working directory (repo root) at run time. For example: ```yaml - id: terraform_tflint diff --git a/infracost_breakdown.sh b/infracost_breakdown.sh new file mode 100755 index 000000000..c088b7733 --- /dev/null +++ b/infracost_breakdown.sh @@ -0,0 +1,163 @@ +#!/usr/bin/env bash +set -eo pipefail + +function main { + common::initialize + common::parse_cmdline "$@" + infracost_breakdown_ "${HOOK_CONFIG[*]}" "${ARGS[*]}" +} + +function common::colorify { + # Colors. Provided as first string to first arg of function. + # shellcheck disable=SC2034 + local -r red="$(tput setaf 1)" + # shellcheck disable=SC2034 + local -r green="$(tput setaf 2)" + # shellcheck disable=SC2034 + local -r yellow="$(tput setaf 3)" + # Color reset + local -r RESET="$(tput sgr0)" + + # Params start # + local COLOR="${!1}" + local -r TEXT=$2 + # Params end # + + if [ "$PRE_COMMIT_COLOR" = "never" ]; then + COLOR=$RESET + fi + + echo -e "${COLOR}${TEXT}${RESET}" +} + +function common::initialize { + local SCRIPT_DIR + # get directory containing this script + SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" + + # source getopt function + # shellcheck source=lib_getopt + . "$SCRIPT_DIR/lib_getopt" +} + +# common global arrays. +# Populated in `parse_cmdline` and can used in hooks functions +declare -a ARGS=() +declare -a HOOK_CONFIG=() +declare -a FILES=() +function common::parse_cmdline { + local argv + argv=$(getopt -o a:,h: --long args:,hook-config: -- "$@") || return + eval "set -- $argv" + + for argv; do + case $argv in + -a | --args) + shift + ARGS+=("$1") + shift + ;; + -h | --hook-config) + shift + HOOK_CONFIG+=("$1;") + shift + ;; + --) + shift + FILES=("$@") + break + ;; + esac + done +} + +function infracost_breakdown_ { + local -r hook_config="$1" + local args + read -r -a args <<< "$2" + + # Get hook settings + IFS=";" read -r -a checks <<< "$hook_config" + + if [ "$PRE_COMMIT_COLOR" = "never" ]; then + args+=("--no-color") + fi + + local RESULTS + RESULTS="$(infracost breakdown "${args[@]}" --format json)" + local API_VERSION + API_VERSION="$(jq -r .version <<< "$RESULTS")" + + if [ "$API_VERSION" != "0.2" ]; then + common::colorify "yellow" "WARNING: Hook supports Infracost API version \"0.2\", got \"$API_VERSION\"" + common::colorify "yellow" " Some things may not work as expected" + fi + + local dir + dir="$(jq '.projects[].metadata.vcsSubPath' <<< "$RESULTS")" + echo -e "\nRunning in $dir" + + local have_failed_checks=false + + for check in "${checks[@]}"; do + # $hook_config receives string like '1 > 2; 3 == 4;' etc. + # It gets split by `;` into array, which we're parsing here ('1 > 2' ' 3 == 4') + # Next line removes leading spaces, just for fancy output reason. + check=$(echo "$check" | sed 's/^[[:space:]]*//') + + operation="$(echo "$check" | grep -oE '[!<>=]+')" + IFS="$operation" read -r -a jq_check <<< "$check" + real_value="$(jq "${jq_check[0]}" <<< "$RESULTS")" + compare_value="${jq_check[1]}${jq_check[2]}" + # Check types + jq_check_type="$(jq -r "${jq_check[0]} | type" <<< "$RESULTS")" + compare_value_type="$(jq -r "$compare_value | type" <<< "$RESULTS")" + # Fail if comparing different types + if [ "$jq_check_type" != "$compare_value_type" ]; then + common::colorify "yellow" "Warning: Comparing values with different types may give incorrect result" + common::colorify "yellow" " Expression: $check" + common::colorify "yellow" " Types in the expression: [$jq_check_type] $operation [$compare_value_type]" + common::colorify "yellow" " Use 'tonumber' filter when comparing costs (e.g. '.totalMonthlyCost|tonumber')" + have_failed_checks=true + continue + fi + # Fail if string is compared not with `==` or `!=` + if [ "$jq_check_type" == "string" ] && { + [ "$operation" != '==' ] && [ "$operation" != '!=' ] + }; then + common::colorify "yellow" "Warning: Wrong comparison operator is used in expression: $check" + common::colorify "yellow" " Use 'tonumber' filter when comparing costs (e.g. '.totalMonthlyCost|tonumber')" + common::colorify "yellow" " Use '==' or '!=' when comparing strings (e.g. '.currency == \"USD\"')." + have_failed_checks=true + continue + fi + + # Compare values + check_passed="$(echo "$RESULTS" | jq "$check")" + + status="Passed" + color="green" + if ! $check_passed; then + status="Failed" + color="red" + have_failed_checks=true + fi + + # Print check result + common::colorify $color "$status: $check\t\t$real_value $operation $compare_value" + done + + # Fancy informational output + currency="$(jq -r '.currency' <<< "$RESULTS")" + + echo -e "\nSummary: $(jq -r '.summary' <<< "$RESULTS")" + + echo -e "\nTotal Monthly Cost: $(jq -r .totalMonthlyCost <<< "$RESULTS") $currency" + echo "Total Monthly Cost (diff): $(jq -r .projects[].diff.totalMonthlyCost <<< "$RESULTS") $currency" + + if $have_failed_checks; then + exit 1 + fi +} + +[[ ${BASH_SOURCE[0]} != "$0" ]] || main "$@" From 3d5a882a53f5e446b10e326db0087c2ee58608c8 Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Tue, 26 Oct 2021 15:35:55 +0300 Subject: [PATCH 159/214] docs: Clarify docs for terraform_tfsec hook (#266) --- README.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 46ace4701..f81b29b4c 100644 --- a/README.md +++ b/README.md @@ -470,13 +470,21 @@ Example: --no-color -e aws-s3-enable-bucket-logging,aws-s3-specify-public-access-block ``` -4. Like terraform_tflint, `__GIT_WORKING_DIR__` can be used when specifying files relative to the git working directory: -Example: +4. When you have multiple directories and want to run `tfsec` in all of them and share a single config file - use the `__GIT_WORKING_DIR__` placeholder. It will be replaced by `terraform_tfsec` hooks with Git working directory (repo root) at run time. For example: + + ```yaml + - id: terraform_tfsec + args: + - --args=--config-file=__GIT_WORKING_DIR__/.tfsec.json + ``` + + Otherwise, will be used files that located in sub-folders: ```yaml - id: terraform_tfsec - args: [--args=--config-file=__GIT_WORKING_DIR__/.tfsec.json] + args: + - --args=--config-file=.tfsec.json ``` ### terraform_validate From 9656159025264c0d50032a48d0e58ad3c15f388b Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Tue, 26 Oct 2021 14:39:23 +0200 Subject: [PATCH 160/214] docs: Pre-release 1.53 (#267) --- README.md | 115 ++++++++++++++++++++++-------------------------------- 1 file changed, 47 insertions(+), 68 deletions(-) diff --git a/README.md b/README.md index f81b29b4c..4ff527ad1 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Github tag](https://img.shields.io/github/tag/antonbabenko/pre-commit-terraform.svg)](https://github.com/antonbabenko/pre-commit-terraform/releases) ![maintenance status](https://img.shields.io/maintenance/yes/2021.svg) [![Help Contribute to Open Source](https://www.codetriage.com/antonbabenko/pre-commit-terraform/badges/users.svg)](https://www.codetriage.com/antonbabenko/pre-commit-terraform) -Want to Contribute? Check [open issues](https://github.com/antonbabenko/pre-commit-terraform/issues?q=label%3A%22good+first+issue%22+is%3Aopen+sort%3Aupdated-desc) and [contributing notes](/.github/CONTRIBUTING.md). +Want to contribute? Check [open issues](https://github.com/antonbabenko/pre-commit-terraform/issues?q=label%3A%22good+first+issue%22+is%3Aopen+sort%3Aupdated-desc) and [contributing notes](/.github/CONTRIBUTING.md). * [How to install](#how-to-install) * [1. Install dependencies](#1-install-dependencies) @@ -29,19 +29,9 @@ Want to Contribute? Check [open issues](https://github.com/antonbabenko/pre-comm -* [`pre-commit`](https://pre-commit.com/#install), - [`terraform`](https://www.terraform.io/downloads.html), - [`git`](https://git-scm.com/downloads), - POSIX compatible shell, - Internet connection (on first run), - x86_64 compatible operation system, - Some hardware where this OS will run, - Electricity for hardware and internet connection, - Some basic physical laws, - Hope that it all will works. -

+* [`pre-commit`](https://pre-commit.com/#install) * [`checkov`](https://github.com/bridgecrewio/checkov) required for `checkov` hook. -* [`terraform-docs`](https://github.com/terraform-docs/terraform-docs) required for `terraform_docs` hooks. +* [`terraform-docs`](https://github.com/terraform-docs/terraform-docs) required for `terraform_docs` hook. * [`terragrunt`](https://terragrunt.gruntwork.io/docs/getting-started/install/) required for `terragrunt_validate` hook. * [`terrascan`](https://github.com/accurics/terrascan) required for `terrascan` hook. * [`TFLint`](https://github.com/terraform-linters/tflint) required for `terraform_tflint` hook. @@ -51,17 +41,16 @@ Want to Contribute? Check [open issues](https://github.com/antonbabenko/pre-comm
Docker
-If no `--build-arg` is specified, then the latest versions of `pre-commit` and `terraform` will be installed. +When `--build-arg` is not specified, the latest version of `pre-commit` and `terraform` will be installed. ```bash git clone git@github.com:antonbabenko/pre-commit-terraform.git cd pre-commit-terraform -# Install all tools with latest versions: +# Install the latest versions of all the tools docker build -t pre-commit --build-arg INSTALL_ALL=true . ``` -You can specify needed tool versions by providing `--build-arg`'s. -If you'd like you can use the `latest` versions: +To install a specific version of individual tools, define it using `--build-arg` arguments or set it to `latest`: ```bash docker build -t pre-commit \ @@ -77,19 +66,17 @@ docker build -t pre-commit \ . ``` -To disable the pre-commit color output, set `-e PRE_COMMIT_COLOR=never`. +Set `-e PRE_COMMIT_COLOR=never` to disable the color output in `pre-commit`.
MacOS
-[`coreutils`](https://formulae.brew.sh/formula/coreutils) required for `terraform_validate` hook on macOS (due to use of `realpath`). +[`coreutils`](https://formulae.brew.sh/formula/coreutils) is required for `terraform_validate` hook on MacOS (due to use of `realpath`). ```bash brew install pre-commit terraform-docs tflint tfsec coreutils checkov terrascan infracost jq -terrascan init -infracost register ```
@@ -163,25 +150,22 @@ EOF ### 4. Run -After the pre-commit hook has been installing you can run it manually on all files in the repository. - -Local installation: +Execute this command to run `pre-commit` on all files in the repository (not only changed files): ```bash pre-commit run -a ``` -Docker: +Or, using Docker: ```bash docker run -v $(pwd):/lint -w /lint pre-commit run -a ``` -> You be able to list tools versions when needed -> -> ```bash -> TAG=latest && docker run --entrypoint cat pre-commit:$TAG /usr/bin/tools_versions_info -> ``` +Execute this command to list the versions of the tools in Docker: +```bash +docker run --entrypoint cat pre-commit:latest /usr/bin/tools_versions_info +``` ## Available Hooks @@ -191,16 +175,16 @@ There are several [pre-commit](https://pre-commit.com/) hooks to keep Terraform | Hook name | Description | Dependencies
[Install instructions here](#1-install-dependencies) | | ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | | `checkov` | [checkov](https://github.com/bridgecrewio/checkov) static analysis of terraform templates to spot potential security issues. [Hook notes](#checkov) | `checkov`
Ubuntu deps: `python3`, `python3-pip` | -| `infracost_breakdown` | Check how much your infra costs with [infracost](https://github.com/infracost/infracost). [Hook notes](#infracost_breakdown) | `infracost`, `jq`, [Infracost API key](https://www.infracost.io/docs/#2-get-api-key), Internet connection +| `infracost_breakdown` | Check how much your infra costs with [infracost](https://github.com/infracost/infracost). [Hook notes](#infracost_breakdown) | `infracost`, `jq`, [Infracost API key](https://www.infracost.io/docs/#2-get-api-key) | `terraform_docs_replace` | Runs `terraform-docs` and pipes the output directly to README.md | `python3`, `terraform-docs` | | `terraform_docs_without_`
`aggregate_type_defaults` | Inserts input and output documentation into `README.md` without aggregate type defaults. Hook notes same as for [terraform_docs](#terraform_docs) | `terraform-docs` | | `terraform_docs` | Inserts input and output documentation into `README.md`. Recommended. [Hook notes](#terraform_docs) | `terraform-docs` | -| `terraform_fmt` | Rewrites all Terraform configuration files to a canonical format. [Hook notes](#terraform_fmt) | - | +| `terraform_fmt` | Reformat all Terraform configuration files to a canonical format. [Hook notes](#terraform_fmt) | - | | `terraform_providers_lock` | Updates provider signatures in [dependency lock files](https://www.terraform.io/docs/cli/commands/providers/lock.html). [Hook notes](#terraform_providers_lock) | - | | `terraform_tflint` | Validates all Terraform configuration files with [TFLint](https://github.com/terraform-linters/tflint). [Available TFLint rules](https://github.com/terraform-linters/tflint/tree/master/docs/rules#rules). [Hook notes](#terraform_tflint). | `tflint` | -| `terraform_tfsec` | [TFSec](https://github.com/liamg/tfsec) static analysis of terraform templates to spot potential security issues. [Hook notes](#terraform_tfsec) | `tfsec` | +| `terraform_tfsec` | [TFSec](https://github.com/aquasecurity/tfsec) static analysis of terraform templates to spot potential security issues. [Hook notes](#terraform_tfsec) | `tfsec` | | `terraform_validate` | Validates all Terraform configuration files. [Hook notes](#terraform_validate) | - | -| `terragrunt_fmt` | Rewrites all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) to a canonical format. | `terragrunt` | +| `terragrunt_fmt` | Reformat all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) to a canonical format. | `terragrunt` | | `terragrunt_validate` | Validates all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) | `terragrunt` | | `terrascan` | [terrascan](https://github.com/accurics/terrascan) Detect compliance and security violations. | `terrascan` | @@ -223,15 +207,11 @@ For [checkov](https://github.com/bridgecrewio/checkov) you need to specify each ### infracost_breakdown -`infracost_breakdown` build on top of the `infracost breakdown` command. It, if needed, runs `terraform init`, `terraform plan` and calls `infracost` API - so this hook can run up to several minutes. - -Unlike most other hooks, this one triggers all changes to the files but checks predefined paths each time. - -For example, the hook tracks `--path=./env/dev` and `./env/dev` depend on `./main.tf`. So when you will make changes to `./main.tf` - the hook will run and show the cost changes for `./env/dev`. +`infracost_breakdown` executes `infracost breakdown` command and compare the estimated costs with those specified in the hook-config. `infracost breakdown` normally runs `terraform init`, `terraform plan`, and calls Infracost Cloud Pricing API (remote version or [self-hosted version](https://www.infracost.io/docs/cloud_pricing_api/self_hosted)). +Unlike most other hooks, this hook triggers once if there are any changed files in the repository. -1. `infracost_breakdown` supports custom arguments so you can pass [supported flags](https://www.infracost.io/docs/#useful-options). - The following example only shows costs: +1. `infracost_breakdown` supports [all `infracost breakdown` arguments](https://www.infracost.io/docs/#useful-options). The following example only shows costs: ```yaml - id: infracost_breakdown @@ -257,7 +237,7 @@ For example, the hook tracks `--path=./env/dev` and `./env/dev` depend on `./mai -2. You can provide limitations when the hook should fail: +2. (Optionally) Define `cost constrains` the hook should evaluate successfully in order to pass: ```yaml - id: infracost_breakdown @@ -280,7 +260,7 @@ For example, the hook tracks `--path=./env/dev` and `./env/dev` depend on `./mai Summary: { "unsupportedResourceCounts": { - "aws_sns_topic_subscription": 1 + "aws_sns_topic_subscription": 1 } } @@ -290,24 +270,23 @@ For example, the hook tracks `--path=./env/dev` and `./env/dev` depend on `./mai - * Hook uses `jq` to parse `infracost` output, so paths to values like `.totalHourlyCost` and `.totalMonthlyCost` should be in jq-compatible format. - To check available structure use `infracost breakdown -p PATH_TO_TF_DIR --format json | jq -r . > infracost.json`. And play with it on [jqplay.org](https://jqplay.org/) + * Only one path per one hook (`- id: infracost_breakdown`) is allowed. + * Set `verbose: true` to see cost even when the checks are passed. + * Hook uses `jq` to process the cost estimation report returned by `infracost breakdown` command + * Expressions defined as `--hook-config` argument should be in a jq-compatible format (e.g. `.totalHourlyCost`, `.totalMonthlyCost`) + To study json output produced by `infracost`, run the command `infracost breakdown -p PATH_TO_TF_DIR --format json`, and explore it on [jqplay.org](https://jqplay.org/). * Supported comparison operators: `<`, `<=`, `==`, `!=`, `>=`, `>`. * Most useful paths and checks: - * `.totalHourlyCost` (same to `.projects[].breakdown.totalHourlyCost`) - show total hourly infra cost - * `.totalMonthlyCost` (same to `.projects[].breakdown.totalMonthlyCost`) - show total monthly infra cost - * `.projects[].diff.totalHourlyCost` - show hourly cost diff between existing infra and tf plan - * `.projects[].diff.totalMonthlyCost` - show monthly cost diff between existing infra and tf plan - * `.diffTotalHourlyCost` (for Infracost version 0.9.12 or newer) or `[.projects[].diff.totalMonthlyCost | select (.!=null) | tonumber] | add > 1000` (for Infracost older than 0.9.12): - * fail if changes push the total monthly cost estimate above $1K - * fail if changes increase the cost by $1K. - * You can set up only one path per one hook (`- id: infracost_breakdown`) - this is an `infracost` limitation. - * Set `verbose: true` to see cost even when the checks are passed. - * To disable hook color output, set `PRE_COMMIT_COLOR=never` env var + * `.totalHourlyCost` (same as `.projects[].breakdown.totalHourlyCost`) - show total hourly infra cost + * `.totalMonthlyCost` (same as `.projects[].breakdown.totalMonthlyCost`) - show total monthly infra cost + * `.projects[].diff.totalHourlyCost` - show the difference in hourly cost for the existing infra and tf plan + * `.projects[].diff.totalMonthlyCost` - show the difference in monthly cost for the existing infra and tf plan + * `.diffTotalHourlyCost` (for Infracost version 0.9.12 or newer) or `[.projects[].diff.totalMonthlyCost | select (.!=null) | tonumber] | add` (for Infracost older than 0.9.12) + * To disable hook color output, set `PRE_COMMIT_COLOR=never` env var. 3. **Docker usage**. In `docker build` or `docker run` command: - * You need to provide [Infracost API key](https://www.infracost.io/docs/integrations/environment_variables/#infracost_api_key) via `-e INFRACOST_API_KEY=`. By default it is saved in `~/.config/infracost/credentials.yml` - * Set `-e INFRACOST_SKIP_UPDATE_CHECK=true` to skip the Infracost update check; can be useful in CI/CD systems. [Doc](https://www.infracost.io/docs/integrations/environment_variables/#infracost_skip_update_check) + * You need to provide [Infracost API key](https://www.infracost.io/docs/integrations/environment_variables/#infracost_api_key) via `-e INFRACOST_API_KEY=`. By default, it is saved in `~/.config/infracost/credentials.yml` + * Set `-e INFRACOST_SKIP_UPDATE_CHECK=true` to [skip the Infracost update check](https://www.infracost.io/docs/integrations/environment_variables/#infracost_skip_update_check) if you use this hook as part of your CI/CD pipeline. ### terraform_docs @@ -321,12 +300,12 @@ For example, the hook tracks `--path=./env/dev` and `./env/dev` depend on `./mai if they are present in `README.md`. -2. It is possible to pass additional arguments to shell scripts when using `terraform_docs` and `terraform_docs_without_aggregate_type_defaults`. Send pull-request with the new hook if something is missing. +2. It is possible to pass additional arguments to shell scripts when using `terraform_docs` and `terraform_docs_without_aggregate_type_defaults`. 3. It is possible to automatically: - * create docfile (and PATH to it) - * extend existing doc files by appending markers to the end of the file (see item 1) - * use different than `README.md` docfile name. + * create documentation file + * extend existing documentation file by appending markers to the end of the file (see item 1 above) + * use different filename for the documentation (default is `README.md`) ```yaml - id: terraform_docs @@ -336,7 +315,7 @@ For example, the hook tracks `--path=./env/dev` and `./env/dev` depend on `./mai - --hook-config=--create-file-if-not-exist=true # Boolean. true or false ``` -4. You can provide arguments to terraform_doc. Eg. for [configuration](https://github.com/terraform-docs/terraform-docs/blob/master/docs/user-guide/configuration.md#usage): +4. You can provide [any configuration available in `terraform-docs`](https://terraform-docs.io/user-guide/configuration/) as argument to `terraform_doc` hook, for example: ```yaml - id: terraform_docs @@ -347,7 +326,7 @@ For example, the hook tracks `--path=./env/dev` and `./env/dev` depend on `./mai ```yaml - id: terraform_docs - args: + args: - tfvars hcl --output-file terraform.tfvars.model . ``` @@ -381,7 +360,7 @@ Example: 1. The hook requires Terraform 0.14 or later. 2. The hook invokes two operations that can be really slow: * `terraform init` (in case `.terraform` directory is not initialised) - * `terraform providers lock`. + * `terraform providers lock` Both operations require downloading data from remote Terraform registries, and not all of that downloaded data or meta-data is currently being cached by Terraform. @@ -406,7 +385,7 @@ Example: # Reload shell and use `rm_terraform` command in the repo root ``` - `terraform_providers_lock` hook will try to reinitialize them before running the `terraform providers lock` command. + `terraform_providers_lock` hook will try to reinitialize directories before running the `terraform providers lock` command. ### terraform_tflint @@ -449,7 +428,7 @@ Example: directory, ignoring any other folders at the root level 2. To ignore specific warnings, follow the convention from the -[documentation](https://github.com/liamg/tfsec#ignoring-warnings). +[documentation](https://github.com/aquasecurity/tfsec#ignoring-warnings). Example: @@ -460,7 +439,7 @@ Example: } ``` -3. `terraform_tfsec` supports custom arguments so you can pass supported `--no-color` or `--format` (output), `-e` (exclude checks) flags: +3. `terraform_tfsec` supports custom arguments, so you can pass supported `--no-color` or `--format` (output), `-e` (exclude checks) flags: ```yaml - id: terraform_tfsec @@ -522,7 +501,7 @@ Example: `terraform_validate` hook will try to reinitialize them before running the `terraform validate` command. - **Warning:** If you use Terraform workspaces, DO NOT use this workaround ([details](https://github.com/antonbabenko/pre-commit-terraform/issues/203#issuecomment-918791847)). Wait to [`force-init`](https://github.com/antonbabenko/pre-commit-terraform/issues/224) option implementation + **Warning:** If you use Terraform workspaces, DO NOT use this workaround ([details](https://github.com/antonbabenko/pre-commit-terraform/issues/203#issuecomment-918791847)). Wait to [`force-init`](https://github.com/antonbabenko/pre-commit-terraform/issues/224) option implementation. ## Authors From bb880b0f91dffb4d9d23651d87ce79f05326cd52 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Tue, 26 Oct 2021 14:40:17 +0200 Subject: [PATCH 161/214] Updated CHANGELOG --- CHANGELOG.md | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3e5ad6dd..493676ff7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,29 @@ All notable changes to this project will be documented in this file. + +## [v1.53.0] - 2021-10-26 + +- docs: Pre-release 1.53 ([#267](https://github.com/antonbabenko/pre-commit-terraform/issues/267)) +- docs: Clarify docs for terraform_tfsec hook ([#266](https://github.com/antonbabenko/pre-commit-terraform/issues/266)) +- feat: Add infracost_breakdown hook ([#252](https://github.com/antonbabenko/pre-commit-terraform/issues/252)) +- feat: Set up PR reviewers automatically ([#258](https://github.com/antonbabenko/pre-commit-terraform/issues/258)) +- docs: fix protocol to prevent MITM ([#257](https://github.com/antonbabenko/pre-commit-terraform/issues/257)) +- feat: add __GIT_WORKING_DIR__ to tfsec ([#255](https://github.com/antonbabenko/pre-commit-terraform/issues/255)) +- docs: Add missing space in terrascan install cmd ([#253](https://github.com/antonbabenko/pre-commit-terraform/issues/253)) +- fix: command not found ([#251](https://github.com/antonbabenko/pre-commit-terraform/issues/251)) +- fix: execute tflint once in no errors ([#250](https://github.com/antonbabenko/pre-commit-terraform/issues/250)) +- docs: fix deps ([#249](https://github.com/antonbabenko/pre-commit-terraform/issues/249)) +- feat: Add `terraform_docs` hook settings ([#245](https://github.com/antonbabenko/pre-commit-terraform/issues/245)) +- fix: terrafrom_tflint ERROR output for files located in repo root ([#243](https://github.com/antonbabenko/pre-commit-terraform/issues/243)) +- feat: Add support for specify terraform-docs config file ([#244](https://github.com/antonbabenko/pre-commit-terraform/issues/244)) +- docs: Document hooks dependencies ([#247](https://github.com/antonbabenko/pre-commit-terraform/issues/247)) +- feat: Allow passing of args to terraform_fmt ([#147](https://github.com/antonbabenko/pre-commit-terraform/issues/147)) +- docs: Add terraform_fmt usage instructions and how-to debug script with args ([#242](https://github.com/antonbabenko/pre-commit-terraform/issues/242)) +- fix: TFSec outputs the same results multiple times ([#237](https://github.com/antonbabenko/pre-commit-terraform/issues/237)) +- chore: Do not mark issues and PR's in milestone as stale ([#241](https://github.com/antonbabenko/pre-commit-terraform/issues/241)) + + ## [v1.52.0] - 2021-10-04 @@ -443,7 +466,8 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.52.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.53.0...HEAD +[v1.53.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.52.0...v1.53.0 [v1.52.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.51.0...v1.52.0 [v1.51.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.50.0...v1.51.0 [v1.50.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.49.0...v1.50.0 From 01a6170d3685e8ec5a553cc720fb738dc6cd59d0 Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Tue, 26 Oct 2021 16:56:41 +0300 Subject: [PATCH 162/214] fix: Fixed args expand in terraform_docs (#260) --- terraform_docs.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/terraform_docs.sh b/terraform_docs.sh index e817687c2..64af6a20e 100755 --- a/terraform_docs.sh +++ b/terraform_docs.sh @@ -5,8 +5,8 @@ main() { initialize_ parse_cmdline_ "$@" # Support for setting relative PATH to .terraform-docs.yml config. - ARGS=${ARGS/--config=/--config=$(pwd)\/} - terraform_docs_ "${HOOK_CONFIG[*]}" "${ARGS[*]}" "${FILES[@]}" + ARGS=${ARGS[*]/--config=/--config=$(pwd)\/} + terraform_docs_ "${HOOK_CONFIG[*]}" "$ARGS" "${FILES[*]}" } initialize_() { From 15cac9087438b51de9a29079bf1156668eaf2593 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Tue, 26 Oct 2021 17:40:24 +0200 Subject: [PATCH 163/214] docs: Added notes about sponsors (#268) --- README.md | 16 ++++++++++++++++ assets/env0.png | Bin 0 -> 10210 bytes assets/infracost.png | Bin 0 -> 7258 bytes 3 files changed, 16 insertions(+) create mode 100644 assets/env0.png create mode 100644 assets/infracost.png diff --git a/README.md b/README.md index 4ff527ad1..dd5daf65b 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,22 @@ Want to contribute? Check [open issues](https://github.com/antonbabenko/pre-commit-terraform/issues?q=label%3A%22good+first+issue%22+is%3Aopen+sort%3Aupdated-desc) and [contributing notes](/.github/CONTRIBUTING.md). +## Sponsors + +
+env0 + +Automated provisioning of Terraform workflows and Infrastructure as Code. + +
+infracost + +Cloud cost estimates for Terraform. + +If you are using `pre-commit-terraform` already or want to support its development and [many other open-source projects](https://github.com/antonbabenko/terraform-aws-devops), please become a [GitHub Sponsor](https://github.com/sponsors/antonbabenko)! + +## Table of content + * [How to install](#how-to-install) * [1. Install dependencies](#1-install-dependencies) * [2. Install the pre-commit hook globally](#2-install-the-pre-commit-hook-globally) diff --git a/assets/env0.png b/assets/env0.png new file mode 100644 index 0000000000000000000000000000000000000000..da3eec1e38f1722e8a82d972f4b26d7c9b21194d GIT binary patch literal 10210 zcmV<8Cmq;{P)_Fv9sc3X@6~%XQDqfX1z4a2mmr`diV(oYZrdcvQXr;7l9d?H45nkuB5_DG zYHUMR(y_&eLedIJG*Drd$$=qifCdGDs)8!&Rn>b{Z}`VMoU?oFbMO8C`w#EcD@2=K zi(Tv1`|ohhJ;Ofx?D^7P{yBfiFfL`nfk*)jhf#N7kE#nSc)($wvA+b=Jcg?P#|3D1QECPL>0PNKb{P)s2d}3fDGWdp>#^2!a zb6%;2u8cKKZ0KTCH{ShYYUqzkdie1_Esj7DjtcI+yNdU`=Q*rd-1Nj5CVZvj=$k=@=s;gs*&MHCC@Kw4Q(Z=>Ttg+itinp-|{p6BD#Q+@2--NeqFO%#ir<&!cFe7}fhvkqev)~_EyrD9O6HZVL~uys_c zRn+Ts`)v37+jWs`qddaB-3Ae$wX`ewy%Z$juzl0xlR^(sHZ2hoRMMwv~3=Y@ZP*y~p-n9x1YO25Yrip4DI!w2w(| z3_k=g1K1^Sx7l~)AmPJ|CO{=y#x;1`lVRg+$EwmVD#AbajMU5U{}sq05xgK$@2%Gx zA14Zjh{#?uroHgHK`EeG4WSg-!x(+#!GpCuLBPK?I7mD43sgvu>8U|p-d#8KSvo-f zrJgU=Ug=u?3rvh-{XsnL58{3>d=U2QedlYv=fgkaO~~@dUS|srfiM?0uKQ>dKBydj zuVLNuj(#xYIQUUbW5nR7Df`Q?a!3#~LAvbdhW0(j4}$s~0RJqJ>evO6-~A^_-XS|n z9)iGZ0Qjbbh2Ux;^J{So?BAatbYI|B`2=AF!TY|h@5T1@o^`=4pduW?;|Pp_k&fxP zUtL2(i0#vz6_%7j#c=WV%EEh~ypK`;_1E)m8gWp)X8|Qmpyl?@m*GVL*G-Oo^v&}U zR?8T^%NX-|p|aVOaJu`eqog`gpCEay7F~Adod@}IpDX?P>8HC(BXl={aMyVrxL;d1 zSpk;PtE5Qsn2VBAhcY!lI|*yS@hd?|Q#kiO((S_~h1G0c!#Y@U{p1U{lZ56YF`7GK z&|1}JnY;`y7I>aQ6dBxq|FlI)?t{@Hzf`Ni$9-SDz7uiT5*x=+KV-&30Or#rk*D(p zAUR(J;0;lvH%c9X(DLNKfRBCqD!+Ee9S83wTJyhGhS0rj>_CwI$U+@Ov%d<2x?wDG zfC!G|L&{LG>Qgq4-bA!WwqJc3lP~E54hWTNBIrYaH?b5`^)kE|Ac&>45v1xXND9Jg zt=BS}-zgT|4hkMcynlSW_)Ep2`>mOodis2K`Q)!L2__5;72eTo2G?jUn|HE+Hf$*1 z3ty;w@H3xL2}1W1Eve%dCk%a0bMum8KS;M0&a)r}+IT291Gcj)9ggE~#73V?U5K3h zq6DMwT4Lq2pEW>nh_G<8#^Mbc-e!6knSa6{2r7tGD0FLAGO(<{FEZ;w7xAt;j(3LV zsola~i%{5)?Z}r2f*6}Njb9I}-uKi~&wW#HOK>QT6(%R+S8du9oVzrI=1v;h(HKt8 zIb%oqU^Hb9c5Wad$p%{C70f6oZK-tV(x(?9zAR!rOmNUMXBU^`DvpZ!{>sD4w&T-j&UCz-HearKlFEg7<^Y76bXQ)OUb|>dN(sF;oD+ncx;S}5RWjMzmWU9Wj5ZD;7)*=(U1)iZH8r^9ww!J-c!I*hpft>5n1yFyWSNe ze3s#@dzm`_w1HWJIqEt>xwEZf&$oOt-R^l-s6(|HTV!1d5=tz*xPyg-Iu0DzFXuPr zg?*(Q(bJ#o{>*D}%|rKNc5n;_g!Icpq!JcGAv+hmk_(((lfzhbSf(3vD7mNPA0--c zTZU0^ouXF5&SOStC?=HH!S~3+@d)9#{h^ z5?U5ZnTH8Mo$=(`HEQ2wl(rLCFHOx~hMx>1#LaY5+aiFet`~+%shx>V*&qz#6w#M% zR-(gZD`2Ku{?i?JVDGsj?z;8(LLA`>7HKKb*(##sZ>mF-dn)BuR7$Bc{sk?hHuf*+ zw5+X9!7#ydd>G#st0AO@0vt;`LlFFwl$(UIH(fYaGr3pCj*rN)3rizrhRji&@x*_P zQU6|w%yG}7FT+m(5`^p#+{NtC&DvJNFs^kVtl*9adCx~MRV}J5kY=b!4>Va2>suGn zb4Ba+L?;FsQ*c|a&an=8mLubp^V$kRXPYx!qP>)7MTjl(n-;qh2p9=uzGE z<6c>*oas*b7BkGQ2;MIa!8@Y_%ri_;A^PVk^rHc2vu~d{67&d#lnr|BcaI*rtF;2I zD9?QcM~s*lnl)m`VG=D#SD6HPbE0q5T?p+A1I>A0=R0H6 zzkf88S+wRX=KjsXdM~ZyA*t;!Ge1d$gKp1AwV_@&sMQQ6Ci?P$C#Q!{qyc6;&W!K1 z`r(jRy~g91Pb8x8bWZZCNd|Dy7~TnBS7w##>6H)^?lEkfK!nrMcZ>7MVE}hzR@?r- zeRatlgG00p!0FO;QAGo&0=V67x~*m}TL*QoT(did_wGTIKD!n!JWJ=rErib=L-;Fu zzd>{ycDsWa$7C0@=4q{YgXbx&6fTIO;L7iQw|=^m z@qOxRY13LmYr!4fYYe^?Me(NqXHO;+rTn9{oMp%^w~WEXTH`})tDGP!{iZSWcm0r7 zG*HGkh-1I6Uxhd~`92qb#@)=kKDV>f9EUdnm`&fC>Wx{%e<1o4&u_ld`V!7=f%NRVHr$SlYmr{{vf;~FRgKt*uT2cB*+ zR192sqQXp7;UAtRtS%e4B|_OKlnM%7?4n#K;&r1g4xo+*gYe*^i3Voz9QIj}P{DzV zNH1bGs$jKOM$AokP8~x^bRrHEYphYOMd&zPPy<35A_7ffS!7PtMt^4v|3;!NT7E$K z!OTo_qEh^4TE`zQ6x??yrEbVSHZY*@2Y)b$%P-%HIChY8c=J-BC=beUlz5JP%oy__ z(Z6Q8+~vWtBgYV#%MTowzg(&EA36^HA~7x)luAXbWe9@6O8iMb%^k+*59Lr&-t{)6 z+`b4M1R4_)MI3+p1mf8AEUseF!_J-a=kDBDSuaKspk=oHCK1h|#Ts$T$K#05V4zI! zE+0zjJFG#jlXO=I^3xjb>x(ew3?aI05o(jatoF(k*FGUclrv9vA0}<+cmxbaz`iDM z!A6CTovRz_& zF(G?e8~4D0#`V`+GxvcDFC6>y?5wfn$HyIvj1;kceF4p;7E^9dYg#oE_5u7UWBB)N zE4vUmk7$XY!>!-hU!i_lS3d!G8;mLbc6QD4N(h1oajdiF?8VaMM3U7K-h(?1usH{{sGQv1?sBvMfCS%gRPNc49rWPWv7@0*~{G0~8y9B(( z2hYTyZueJV7ernAOJj)dsveO_FT)w;I+IfD&`FRgo+JF~YaG0KARVNZR->~_#z|Gv zzk<-hbS$P%^frr@{MBXGu?*qx8X&9(E&j2iCfk_!^BFR?%BEDB$oW1qKduy2Gn`2u zG3iD)K}4s;vEGcFS2x@!Ir&vWjO`oz;~a8T}1k3VxaFyoXs=2P0P zl&BNo0cJc)M9sv!pA0#(ag=hzhv9X`=wmXZP8NYHtjK#SmDFSjiP@M0K~xwX_RiU|W#ZbX%XwLRiw_^J;7|T!kL_#sZpKVPKhE!DtEdjB9h_+d zl10Q+VJ~bHkS}Esg0S|;LkgxK3}r_8p-Mz%^P;p#xz?LuM1z{Lo7e#tJqr7X^dNOGrQ;P3szf)W1`2M>^{{4hz z)8N2?80*&ge_SrBPlO?B@rO`KrPj8jGeT*7-?w!(o3;PUOxFTD-0qXKF}#upUu0G~ z+rN@}TCdz;4F6$vQmYZRZVfIx|NP+EYE^d-g3(cf#~w5I!4Lctq4O=5Ma^d$6TO04 z-e}h1cPA0e96<=|1wquk8qUQ7P_He3W(-JJrPjYpTPEteL~ZO}G!FehE#Z&7o*FH< z%(zIn7_gQ4!I?0S{jq`qE-^8#l55W`0`EB91x&Lg7ud+G0z1>c>kWb%#87FhUTN zZz&enoO$^0^rLInvLD9@>!K7+W2Q5s$b2V1@!fvA-B7D(D>z)c2hm2L}AJ=I4V)+PjSPmD!+m{MIPK*E6c`x@_B#cJ12$egxn+IT;^! zv3qypypMcj&nS_2x8*?)ya>k`M;NwOxh(8L!G-THUZ}<6kutWx_H~Cb%L+05p~0Zl z4(MIwM4_wsZJNv_3ba4d|H{HtO0O}D8>0QrSMaJL)b=9GqfO96f40s|6&CFw?B2S& zevG)l^f2)8!@x`h81aB_9n@c#a?NXyV>1LiMVx ziXsP#QSFV5p!wNhp+;R)!Dm84M>jm^%xzVUI+Sf^vSOVHLRbt?nbC-HKCCj)QKZkz z^QW>M`@Z>8#|cm_D@#pa35X-1+|d6&!P?VeQvI93-X3%xi%h(j*2Yre9QYTZ;Ne54H{*-VB17zF<9 zdEiTXI8!KZ887g~YlpIJf>ZuD`K4VKaS;5WIiJjQ(W(TP09XtN;w}(|`2jT}%<XOkH^1jhg2i5BE^}qF74NOgqV&g_&VPO$kN2%DGf8IbSqG&A|jR4K2{HA-AQdeZp z3=Jvl-5YMY{`xs_$Fy;M2M>yGgtCjr`ZpT>n(OlKw5hgrB)X7DeU+`?p0rhX=xQ~> zzyJF_TQXf)Q1)OnqqC;ePU0iZ4mamNuSr8?NOHeD#E8vZ4oXjCkX2> z2L8P||H>y7eY0Q+c-lLRnioT}G+M(k<|HTO0lLjf!t#hDQfBuw5qMe%o$5MW@z`^b zQ(HV4$nwC|$LZ~T(PFyQk@Ii}vvB)E$%;f~NnU(x& zFUFpl+Tfy%&*(>3`W5b~vf1G(nwGLAZ%+I^WN4mpOIlPizA0eVDW{e|m8ul1Hn#uU z+I2SisZor0=1C}*=_sr6m8E)KBi>z|6)eGrxdlr#qiD8;pjuOCIsTlH!f_OpO1)Hd zDM+bTWpVLvEqN}f%yGO_cbp-c@Tooug-!N)h9pT;c3_xyrgc$kAqg9W)M_cz`GX&v z!s^wowO~$7xwz`8nScB0R~NRgTjynr;ToX%CZ*h4voN8wB#tENezUQ*dbzgh}nfl9^{`T zRzj`GgX212v05JirCWOFmAc}XNCZR1&;&tfmLeRgHtDKBn-6-esDOk*M_@Fx!Em|s zNmd+$hEbsyC&9;CX$t~rI5}we;a`3;+;>@>V}BTKM>`THC;DNzWWGqLUxA%SI^~{6jtIghi=1%t%^}vV^{t9f zIL|_%aNoLh{xg+IyhY*wld%=S&}&iSAV~D=Z+>$VjfSy{Y1Jx?+i$O3@bJUNYC2jY zNOV(o`;Ml);!Y7c8jD^N0gI7M6$=zwWcE`(AlG_z$YER(mP0X0is3Nz4=U+4tmN}* z9E}&$BAGX92tu zL%1kcX8`Oxm*&si8_nN2#jYn_d5M5+8NOYL)Ont(Ig9w_)T`*g%|QKWpuBBGv@%L~ z-TrJwKj=bg{z*rVi-4w7(Xtu6y785R_+dm*j7EbHhMBQLbj!t?m>1q}HUEkJ{dgkt zdLr{}LB@#*V9%ZgKJt-zM^v`s@P%DID#FCy^kel^jC3`L_zwU-MC`hE#Kt|T0R5ac zF=9)vJz+OC820ZZ?SKo6VyY)w7NOfjJh$0%d*kj<%D8|q7N2RPl4i77AC7K|lNh3Aakk6(0KQ#EG2yhFyPZi16zm|8!5e5h1!wDfJ~` zs|>d#XlZn%0qnVs;g0rH)dQ(>(y$;2QGbSJDabMx5t;Hz`NJOl?NZ8Ab%zSp2N&Xf9(>*Uf3eaVP z;w~TS{&g68-?v|kDxYf_{+w{kMrduhf9{9^t z7l}V9?s`6zz&Mm|N~@hx*HhOr79RnIwkQ1b3SfI2>zzcVvc#HN4g!GJEpz`R1qr(n zqgplTj>IWBDcuM7PXN&gnTpUDzL44Zpp32{eH?q3n;T5Sq#TTm1?Lkroc7LUZM*-< z+~df-vGi1+5W#^kkQn7wELmDMrSq10H^{Vz3Em9GJ44lv_ONAj;MjX}Rs4S;6= zS_eci5JovP;neF`sGA`aS)pQMX7=C!_Fx7P{uB1&T#Vr}cm+AHCv@r$ ztwAk}W&4VS1z_(NfWp*KQ?t$H1@Gm(#uDL3y5HZ=SNW$8WD&Ytj#Dj35@I3Rlv=HL zbFCJfAjqnei#U!>I(&HlYl-mP`S}p9detc2_r6VHn6f13+H3b*uxppL`tEcbn#`iz z=r{2Ft}Ou!#N#mr9xGz%x=}3r-hLa))C1y(wHheh4HRw%^oFI887+ifLI=GhP-1G4 zE;q`IRYjmwAslQl;waG(TP}wfVbNg5TfhQh?7>0n%ds9qXn=7K=5aRW@eZuQq;fF8 zJ|d2+1Bpo;|CuCwtAwE8LTnHI2%)muAUf29JIqUDxmg-sEIv5luc@9Sf#jAhx?i%w zpFY@J7#JL6l*_&qYGISYh_!KeN3~k{-E78_fC29UBImpEtf>7SdE{AZNi_yzwOaFR z-_N^>s&6Qz_V>dWy-e6ZKOjR0*2S3ohf(akbPkQl2sG<;vIQxTEM1p+w)rbT=#?5) zGO$h=46}}G+~4FlW9_8Egz``wkPM}SF)rXqe;#$J*bPLS`iD@2vhr)9d)k5`2Ee!v zhw&=c$16|<31yLNN)Q2HGn%SsBNue65k`b~4k(}OV&a!#9Q)1GbOt7zL>H+SSqbi ze#XPbk4@ppznVrgs^J~>T0}FN1WMmx6mA9Ljmsl5@+`-S4J*P|i*0EGSyh8m1;T)= zAM?l{&?u5M)63>@0Bm{V0eChbw!6zFbxxYHp3zC<$`tt+LWqW41d^)JqBBUPLQ66Q zSoIzsb2moN6^1JJw6X(I|AilUp1M|u)r$Rfmw`kVuPi0Zj17&(OD744v@Z-BFs6}1 zKF*x~Zv^nZi9>tW)uy)Krat{q=gn|f$7u{i7S1yzSlY7 z%}c#Ax~+gqX@NP_T?egp$1S^L%X%-u#Dc-#@ean{?%}}y3{g6sRw5V{gtCYR;Ke{{ z&ZQX6sIxdM{lBKpZ4d-=VT|7~sfqDM{|k|RUv^D060KP?^7HG~4Lv1^Uq(g>ICLm< z9({EGxk_=Gv(Cx~w=(OQo;NY_7Q~e`u4N%h=CpoXh_%-Z!wD6tr-vxad!P}8;jj2` zA6JOBnk34zvyN_{9v=FL%@Y5r4Fn!aT)RQ-Nan0;UjEIHG4FJgol(a#`E$x4aa&4n z)slvbt-8d={H-x`^~h8@6T)9Cr0&d~nS?LBlpyP?gopJ;!O(q+i_ufYm~C<{29ueY z`i0LtQ~jIm+l#p9qA5J^Kyda$519>_Nj;t89m`?J5@Xqqei7$a04IqK8e1cb|7!`W z{-F%xB@|Lm8tCIRRFv%S`FHeF2=wsK%NkZVjq@7T;Di&9d#|fh%|GfgDsGh{-Arup zkwkn`)BbHxfCkC0Ch8QUNp<+d5Fcq@@}d!8QF41Y1c`PMGiYQ2Ea(|Dj0bJpO_A~b ziWNS_XfY!>^_0plv zncs&vKPBu(9;t=xa2)3rt@XCd)m3XgXK)b5?Aq1Xd&LzJvBDRMW8Nh>&H;X zQ%Neapv$n*QBPmPibed`z-R-qx;bo*Vm?;P+7vV5Fh)c%0t#VL0Wcu>L`4%!8W9;p z;;*0#V)2!56tp7CKgVLvm*kCP{Xft!P3!Dpv_!Mu4dTC=S%?yUD51>axZ>v2WtQP- zYTb;Ib)Q+vcvz>|l&V$6=FJ{TCBkEmHEsCR(E(dm&J$0Z#MZ52c<7<$v1`|y_(va| zF7_t~8Ip*lP|VvM@&DGXwzz{Rx}x0zm4$#z000M_NklxwlCpu{l7w;* z4j+~ziG|xQzIb9e$u5@p`5Kb;xm&f)FH(6fUO0Ng3THQxgMo%>WmXaA_r^ z>cSE+Iah7L%1Y0vLS3~O4YIz_MxGI(tZkfZ$1sTs&pVE>aAypRb+mL25UUIwle=~t zk=jf|yM!tT5)Tbg>N9+_UVej6GrPx{Y$x8(!%q-1t(K)pQ37y&v zU{i8M_<#4Uwc5z%m0EmVu~^?~-E~PE|1~bTWbmJ_xMEU#uRH1yDlXw<4?bA6h}|7i zDdnRGPf4I|F6)s9)93lVx_{fY0qoghP_H+a)Zu3d(Z$!rb;~Fe2GM97`Y(BZb{YGT zk-qTdbL;GzP+?VK8`d!9d{h_^OEemj_GNGB>%JOIf&G~oh`F|Bp>iR z-YkCMD;YD{_#`O9^9tg?b(YrplHOwUK+5s=7a3m&w7lfd{y}|7J-VOF`kG$t4C~=E zr5NW9n*w32huw!t*tx5K-Mf=x*RCR-dB**^#Kq+^9x>yi#^8J`Ex^1W5M#=5$4#JI z_EHPmO9j||H7kw_HhE0`h$7kMoqx&3AqfCBLytVN_bvPO&7Ud(403ONKE}3fh1)l8 z_74g2Wk4b12;If-vf7{>R{&PG4G65%`%=3O)zm3P)^&)uH+ zgXe1e;lYEo+j@k;BcR*L;Rp@v=ORS;xWKMf^b?h5ImQWZ9>O1eWdhEc!~_q^*Vz!M zFMi?^&-~%;-Ql*85w}$jGvA^Vu5UE>OGIY3j8WfLE)T9Ts1=KNeq;pJ=Xl$;0;Z>@ z@Zf{f7#{YQE&S7hEa^+)UDrDUuB+~4j&~Ykj+HH40t0sJX#7pNOe4d?juk3p7ZU0$ z_nOVPr=EocZL>zLUahR^viGxgwjIyMFfS>bjZbvO`zo z`BK314x(*MtRHG%JaSPl7+XdJ6(YrBW97?!^rPV3?(Fd8GQH#3XPfU3$}l{vj#`k* zzRL9c^Nn-j*!f1I5nJjk-$0ZImZf49f7)PU??|)|QJ(~n+2Q0jhyoA^zOYH!V&gqI#1?CrN`KN=17`}6bWEWn)B&M%Tg!x&>u zAfgj`3Z@`)mo28m)u7b_?AsUM)KkmXeE7p_ueIM{l$cS+&*kmtZ8Q2BVDjUVJLy-P zd*Z-2r-;w%2RpWqE}psjctDtu;BR?mPC_;d2%inzz^M zu^Jd~T3cm?;4efeAuW+lFg*F>+`7jen@93Vg=8TWi%0Zj?zh?exX`!042i{X(r;#x z`y{&M_x9XGA`G5&*5EBCom85Zi1JJ)od>CZ!v-IxoKnUOH_TbRcfV)SasD}_=q_fQ zk#EBi&CiwR*2hX)3HF&Ggd~nj*KFQA{41|~WlFJ&Wkl6RVq%dH7jD`26GAD(0Kc|s|Y`?RTbXun_Noo4sR=l9Czu10p z0(JY8V$3HnKx%X z8yN`8as2Uxe-dk9h?LgWUSlgYR`!$`oI0mGld)5c|rhp!z=Jt-+KVVBU6~y9>q?YrC)?8t>8=z z(70Q`r{|0WF{T-B(hyWioKzlm9QAc1Z?_O|uY;5KOmCE5OD-}BA$;2f+8eO~AfBdeyqU-Oych(~JQjoAga7~l07*qoM6N<$g3TJ(F#rGn literal 0 HcmV?d00001 diff --git a/assets/infracost.png b/assets/infracost.png new file mode 100644 index 0000000000000000000000000000000000000000..bacbd04705e81b9e75edf46831cdae97e1f91632 GIT binary patch literal 7258 zcmbVR2RK{r_ZOS0)l$?5qN-{{6p7k1R@G{?Rz!k|1hJJ;RMl#%QmZXhwf9~{Q6rkF zS*x@{?V6?PpMKZ#`~RQ+zEAGG?|t9T8K3i>bCM@F+SEkvG&3JF6&2NKxW2X-<=#s< zFEG+mu3_9CF_asVhrTtQib`1N_X*HR1v^kt(E(j8ZhGG|Hi98>?$QV+oFhuw-`#_P zrlPv4=I?<(x}m&*jwokWtcu7+Q=16T)k#I<#uZ~(V-GEqi>rR17s@=)!~z-UhJ-qa zsHp<4`oky++)>^Lpual?i--BEi2R`!Mj8K}mJtE|0r7TI5z+W<5O~wr6sU#sLIJNx zD@Y;1@>hV0P-(CNL>>yg43v`vE6KFd2V@hYVO+_BW+JfKJGNa2`Hhm_Lj= zA!Se)lsgLRji+G2|6n~_aNan)3+}&A{m=5hGe8NgvGG4P{!?Gv-TyIx_tx>H(D*AL z|0x=85#WK6F+<^TK3+(ajxWVc(cf%5U|L=%gg4I10*Ax=y-}uryA0IQ`n@qw;6-Bu z(iQughs3`~p|la+C>0SoSvjzj>=h|FIScR=7+8_Ady=wpFj?6@p~g5TS9HL?K*6$7 zatam{9OZLG;om_idEq;V?KaieZX( zkbj+rYiXH!;n1!a$^za@PZJ2&(NX|I6%?f8q``l-k-szY_?i?~12vZNOjkV~+CtYlLwH{*f~<1oC$+sE8ncCjpAW;LmB- z|91rbl=pK%QI!5SdGRLHbMZ=0EcMW9?tc{BJm= zcl;jy)3hiL|Fk_6meT9IC{4{G>^2t_755xmTf@RXb1gF)@M{PD}rDYxDZB>hj zFs?;#XsC0lYeaHz(ZDp$iK03Kt)pXBC}$STDoK|28=JE#Qd;$8`=a zeMUq54{-6U^^mf4+^>4e-NJ7zCa~w%^^SswDjSv~CHB)VL-y-tK6FHi+lhH5^0Gxa zB&4_)G;*cU*`TNRnf#K9#fx!zEf2XEvIc_BkG&t8?aq&z=Rw`d zv0Facv3f#llQ{D&(IY%@=N5U`{zsQ7%~khW(CKcQGtOaK<^9FHQ-?=K0b6bN(&c%M z2c4!GTjnw!&)krkjxRQX7N`^!%swx^crKN^ve ztQZvbcIj^HD=jK;)+Mo#pG`j?a1`y-ksgXg=qtYB`jjSg#Ne||=c{Gz)@%B7We z8?17ANAUTlNL9D_xoB^9X7dC58$iZ)8pthlwZYg8=Qg%fVu>U+73tDXpI!gu;$Bu0 zLq_)lQGxb_nK}3j4&cZk%y>%O!(knh9wOmyoA5S(q9aB<8D%-Hbkan0=jV0Y%8blx z9x@w^!_Z1Fhb+`c`<#1?4>tR+mu*^I&-j<*uV4UCr-H03f0b)Kh}MR(9}p{(sW|6zFG_1{Ka*Ar{=c8 z_iySl?qE&nmnqo15mtZ2w;*%>ozK2sE&#NU852GZim7V>^5M4?fK1UhU|SshmajFv zv|l55leD{^9puoHLh9dfC^LrHm)O+BjT$4ElcLtp?BYFhy}*8Mp~1xT7#_aq9nOTdsou2k zcwsw4gt{B#eLWXCFEv*%InnaZneYP zJ=0qJIXJ9tiYP#*hoV9v7ew{v~y5;zjpwpX%A2Tb|WN*20 zLlQ77uSp|lw|<+189jS}gK5sN=SPs8s&q&eC9tD)*&*!^Yk9w+s?pPGo39_dFyN|u zSPZ9Sx#3@Y@uQ@{faVgaxyNm0;(FpG7HVtS#&qD>NTV7d``4Byf}3Byz1G`vTqJ!n zllLpdAHM8rwj=mD=(1f(C^&7tq&q-gkU2@?k6#`KJOP|kwN#&R2wWcTlQ@OGOpNyf z@L*3@xs1-gS=dwkwK}-m>r6x7b!6#W;&(R7!yqFHCoT7!t|x+M5%F))mQlE-l*;XP z`NOA7u8bf{nN$E9jq(#>$!p(TLQ5JrP6T(&h6R+xwjjsQE}N1WBe9rfqz`_1fmWZh z!KcJ>|x;tL>Vb^)B_Z7s6Lr_IfSl;v~7kctt!{)JWr%T;#nEc4(bHZshfhwnT zWsf(X``2y#$T!aL#P8J;(%B)@{5|Z|+Kn=&&zk3^(>1k=Q&oq?q8oRFf z|I900({6Q9a-KMuQkak6!&hf6)S!O?X>ZLew6GbNTc=fz8a-#JN*f+=vB>N7NGMeP zG7Sew(@IAiP*WkFacwDOC++w^T2+hg5x?h@Hvf3IG1G8TbZvZJX%uUtQ|>$S(TwB@ zP@OXh+LwP{A_`@e(9#+FW_QOpZ#lP>dLLlyARn)V>jG&DlS*wHCkLdLjVz4Hn4bI1 z+vQp6V*piE!{G}v>|mj^q?#H!487os{g?`=0a{l~1yU_$c$vKTU8VHqm2KhSU={tcWlwBI0nf6JyNVnA&->PVZjGEM;a&~BC=q&9iN|ZmQsUXoF71y= z8qAiMrcR;zX)XaA1VC&W<0eOWMp!4PMSULL?&$C|5AlL<;sa@-^}jeN*bz%vfLBu8 zG7B1ij=uZoqSM`{lRoH1v-#JFR$UzGoKlb=rciwGocY06!?+YJP&a z+`yT@zsQcBmAJ<39nr7d0rf+%LgB%H1G6#~GCy(Ih^%DzVIhnzk*}t>Kzsu^&O$ zy?gM0jU?W~6eKW66JAtq)XBscMRG>a?wma}756Tqq5eB}_T_WD{qxM9;5x!`ngUh@ zSCsEBw;Q^VLq2CJ+sw~be$GOuJ1Wk(kaxO$Kbc@_>Y?z6Ep9!Ze-Q^V$QEt(IpN4-SlSmWP*me5H+n-`yCS@r* zs?n__?sqJ=f}6^i=BDP>8et zM(JR7Q%wjF5TB^TykoWTqmIB46x1Y@7(vc(olx2r$VlObq)~;R?SS8xk0g6R_VF%=`$u78_F-K z$(5H$^GUcZQ*U!8f(nTwNt%j4dsuE5hKbiCLyz$;2lN|1-snvE?@O(MgE-Q_;R=a_ zKBTALfVgCLfZ$01?>g#QYI<>p>`Rv;(2LdkEh$SET3LAt%aVzole$_i1sbk)qldE> zT2vV7>vXplK^2hmr-Muq$G@Y8r3VxkN6xF_nsre_cI<0veyko2?li-EWF;*}Hize3 zj?phU$5LGNcdqcQr_aRb(&QTt6W}itpJdVTP7A@C-$7Fin@#7Z+xxsSG(*wmPxkh! z3P=w#GAX#_nU$|Tft<5RN;e!Ex*iR53l#5HwawZpBI#R6RAhPk`Rd93;37Of0iJy< zw)Qnsir{aXmAb#~7##+xP*lv=!;n{VR*;t_^fCsCQ=n*D*%|(1!k4D5@A1AYg2dDv z4G8!97V5-Xea*{w>#6`L^2w&?xF^};!_Q-$^XFfnxN>*)Y0rzeJnQYh)DUoLnXp2D zc78O~fBx>REpeh>eT;m(pPe3f4i@XLueN!U+c;T`kXl#!5g)F*DK8=>-{M=f=1aF(cN3!uZn zP3OUP znDLSP(N|XbHJJ|Q@bOCd`}HYDp6>E=`aX_bk&{b|_NurSQEWvEz>KT0V$o7Gf|qf; zbV1|LY%c0>?F1ufokKB{Z+ay>w@eq0e%gH)M_>J^5My4_3B3(y6jAN$t^1kpI>kCQ zTh>46E-e}0%m{Gqeq)Wf;|KAjS!2o^DN2}#Q5iG3R$_ZZGu~irSincDrSk_bRnEr> z!(9_YofrT|$toto9$Ji_?_VxzqJAgn6%YC39&)`Ey_k(cKM7x7_r2%>1!r&Jqb<=> z48DK8QWsr)~1mI=c;dkL%s%&)p!yt&R=flhU+})F00i6o|YJF zT^?{Tah7*?26R@Mg!;|lGjf|<3zJ@@7@I}~6cbMiCn+R7#8rQ~npT!=Hg%GTW`&aL z1#aG2FLa8mP@XZ&k&#Ct)3*^n1JA*nZ=6LEgl~IR0n-rOzc^)R1x~Ln@Na9JG1jfp zH)rs%m2D~?@scYh_b#M|BpfM}IBx`;2%0$lfQ=I_;k1 zE?x)@idH#GJ!@c|WplplC8wciNnfNe+%J90{9S8m{$5-KnB=1E<8`$Q95*?roeo-N zF;E4spM`PeLcwxix)~48R}QZsp0?VN!5k5hoRBYOMTyw^Bav3cD@#&#wL<8r^lKr> zK;5UQYF^JwGm2fzOy09hS~aZFQTUGYt=rvOPVPdJk>54d^?-IyFIh=3slk3#AR?4?4>`Y)eYoDJDFvGK3|3e z55}IU{XWG{!x`1{;)&ti$`NLn6z$xK-JhS9V$CYpv8Paq8xq>N+u706lX1yd;tOBt^{HQHT+ z?)}L8GN8=yg~cnP%Lff3V21`G2w*KSr>F48_rZNE8r-Nov1?X~qXK&OjSA1L3^j^f z7cr-I%+V|#&@kKCT2@*A@uEvj)8h2@h$7$Eo)X-4VuIvfJNwcVC8jt3r2-t(geTec z_0;C65}zD+q$l(48#HTFDmRilOu$u=av2PjcWZ10<&dd)T7B+}Z>$!P@3H6B8tLkF zE~)NAGI~KV1LHJGRQ?2OaXeppP!_~(n+{l+yw{BBo{O1H z1xFkWeYq*^YwT)O$t1N4aPJG_H@-Kr+dq?!1d;Tz7Gft2ex9KsAHbhhl48?;6s~3x zvGVmitK9~tE`PwsUsKl0EglUV+OIkhBKV64xX*t0m>PaO3RPrexj%i~4A5p)nY8_D zV0rxd^anNCy}{-X%uvji%wbD(Bq+UnkaTJ^H^S_-)8>Opc@Eq2MN2kSMr(m*r<(79 zdq*;s2!OyY)==`-_=tmfUNrxgmeN$EQihR^m_`1L0(*D)9n{9I-3y2#(W?R)QQD2C3^Uj>YtJE}!Pn#U9~haq>i_?yXz1-Im~dYhxDqV)Nh z4`X=^KYVnli}O`}oc|tsyN%b`H>mHXP?i+^%^1GSVda)FvY0g9#6I~TN^s#*WJDpU zTB{cne2u2e!-`#q(wr+VRC!THgWD*$)pC>p+S4RsjB5|uUQUl1;zz`j^z}kRaNTTSBXedBc8N<{ z?N}8@Sk53uc;G-Qp=S>;Bvn&$+GesUOw2-+e_!A933ts4oBI~c)?DGXI`LLex>lPH zALm2-3J20srtkD@8=PeE9@nf?)h$*4d`m`VU+T5ky7l1uv5!8q(5OsiBqz=qzC>Jm@5#9)kCGP<-8%LW?SJ^mt^a&J zR^}K7Xp1m#*ziM9`_$!hU3>Yc?epk7B8%e)N}~MqbKL$^xn|#IP_GyR@aDrzzg|YM z8SQDd4MV5hx--IhKCaMcao$jNK5@*qL}z}p`N%kLfKk>FRn2EdeH*|J_+Qw(HAm~v z-229K@4Nlwi8{Aq;zX)W-P&wnRyS5p%5cnJuWI^N&&{dW;FDmY0 zw@SK%mecgf96KH7)iwY2=X6KbkA+E}RhVq^Fuhvt8q5gJ(P1aiN*^B~MdW)uOO+Ao4N_RYHkJvodE6O`rDcWTC`|qmGJn%yfwG(7& zFnxiA+=b-=L{01rF`JEnUby#`#7DzBj$aSsGJSgy{Id5{-4xw_6?q?-e3oHIDs|kw zY-Lu%d?ltM$TB{LU0Z#$n=mgaHfP<0N`8y?`zoky- Date: Wed, 27 Oct 2021 14:45:25 +0300 Subject: [PATCH 164/214] feat: Add support for quoted values in `infracost_breakdown` `--hook-config` (#269) --- README.md | 12 ++++++------ infracost_breakdown.sh | 22 +++++++++++++++++++++- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index dd5daf65b..24aaedb67 100644 --- a/README.md +++ b/README.md @@ -259,10 +259,10 @@ Unlike most other hooks, this hook triggers once if there are any changed files - id: infracost_breakdown args: - --args=--path=./env/dev - - --hook-config=.totalHourlyCost|tonumber > 0.1 - - --hook-config=.totalHourlyCost|tonumber > 1 - - --hook-config=.projects[].diff.totalMonthlyCost|tonumber != 10000 - - --hook-config=.currency == "USD" + - --hook-config='.totalHourlyCost|tonumber > 0.1' + - --hook-config='.totalHourlyCost|tonumber > 1' + - --hook-config='.projects[].diff.totalMonthlyCost|tonumber != 10000' + - --hook-config='.currency == "USD"' ```
Output @@ -385,8 +385,8 @@ Example: ```yaml - id: terraform_providers_lock args: - - '--args=-platform=windows_amd64' - - '--args=-platform=darwin_amd64' + - --args=-platform=windows_amd64 + - --args=-platform=darwin_amd64 ``` 4. It may happen that Terraform working directory (`.terraform`) already exists but not in the best condition (eg, not initialized modules, wrong version of Terraform, etc.). To solve this problem, you can find and delete all `.terraform` directories in your repository: diff --git a/infracost_breakdown.sh b/infracost_breakdown.sh index c088b7733..ac0fdf911 100755 --- a/infracost_breakdown.sh +++ b/infracost_breakdown.sh @@ -105,7 +105,27 @@ function infracost_breakdown_ { # Next line removes leading spaces, just for fancy output reason. check=$(echo "$check" | sed 's/^[[:space:]]*//') - operation="$(echo "$check" | grep -oE '[!<>=]+')" + # Drop quotes in hook args section. From: + # -h ".totalHourlyCost > 0.1" + # --hook-config='.currency == "USD"' + # To: + # -h .totalHourlyCost > 0.1 + # --hook-config=.currency == "USD" + first_char=${check:0:1} + last_char=${check: -1} + if [ "$first_char" == "$last_char" ] && { + [ "$first_char" == '"' ] || [ "$first_char" == "'" ] + }; then + check="${check:1:-1}" + fi + + operations=($(echo "$check" | grep -oE '[!<>=]{1,2}')) + # Get the very last operator, that is used in comparison inside `jq` query. + # From the example below we need to pick the `>` which is in between `add` and `1000`, + # but not the `!=`, which goes earlier in the `jq` expression + # [.projects[].diff.totalMonthlyCost | select (.!=null) | tonumber] | add > 1000 + operation=${operations[-1]} + IFS="$operation" read -r -a jq_check <<< "$check" real_value="$(jq "${jq_check[0]}" <<< "$RESULTS")" compare_value="${jq_check[1]}${jq_check[2]}" From cf0844cd31cc15ec66bb7cec4056ce8018d05d23 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Wed, 27 Oct 2021 14:40:17 +0200 Subject: [PATCH 165/214] Updated CHANGELOG --- CHANGELOG.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 493676ff7..d6f4d616a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ All notable changes to this project will be documented in this file. + +## [v1.54.0] - 2021-10-27 + +- feat: Add support for quoted values in `infracost_breakdown` `--hook-config` ([#269](https://github.com/antonbabenko/pre-commit-terraform/issues/269)) +- docs: Added notes about sponsors ([#268](https://github.com/antonbabenko/pre-commit-terraform/issues/268)) +- fix: Fixed args expand in terraform_docs ([#260](https://github.com/antonbabenko/pre-commit-terraform/issues/260)) + + ## [v1.53.0] - 2021-10-26 @@ -466,7 +474,8 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.53.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.54.0...HEAD +[v1.54.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.53.0...v1.54.0 [v1.53.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.52.0...v1.53.0 [v1.52.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.51.0...v1.52.0 [v1.51.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.50.0...v1.51.0 From 4c509434e0bd9da72b7f400b0e8a2be01c99bdd8 Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Wed, 27 Oct 2021 18:28:05 +0300 Subject: [PATCH 166/214] fix: Fixed 1.54.0 where `terraform_docs` was broken (#272) --- terraform_docs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform_docs.sh b/terraform_docs.sh index 64af6a20e..1152cb110 100755 --- a/terraform_docs.sh +++ b/terraform_docs.sh @@ -6,7 +6,7 @@ main() { parse_cmdline_ "$@" # Support for setting relative PATH to .terraform-docs.yml config. ARGS=${ARGS[*]/--config=/--config=$(pwd)\/} - terraform_docs_ "${HOOK_CONFIG[*]}" "$ARGS" "${FILES[*]}" + terraform_docs_ "${HOOK_CONFIG[*]}" "$ARGS" "${FILES[@]}" } initialize_() { From 57b94a29ec2f7aed0e0790c5745282ac93e65749 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Wed, 27 Oct 2021 17:28:30 +0200 Subject: [PATCH 167/214] Updated CHANGELOG --- CHANGELOG.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6f4d616a..6fd0963fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ All notable changes to this project will be documented in this file. + +## [v1.55.0] - 2021-10-27 + +- fix: Fixed 1.54.0 where `terraform_docs` was broken ([#272](https://github.com/antonbabenko/pre-commit-terraform/issues/272)) + + ## [v1.54.0] - 2021-10-27 @@ -474,7 +480,8 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.54.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.55.0...HEAD +[v1.55.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.54.0...v1.55.0 [v1.54.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.53.0...v1.54.0 [v1.53.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.52.0...v1.53.0 [v1.52.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.51.0...v1.52.0 From 32dae32273c9a1f4bd20f2425301c669ba5c757c Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Fri, 29 Oct 2021 16:42:05 +0300 Subject: [PATCH 168/214] chore: Updated messages shown in terraform_tflint hook (#274) --- terraform_tflint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform_tflint.sh b/terraform_tflint.sh index 52c57566e..af2040877 100755 --- a/terraform_tflint.sh +++ b/terraform_tflint.sh @@ -66,7 +66,7 @@ tflint_() { # Print checked PATH **only** if TFLint have any messages # shellcheck disable=SC2091 # Suppress error output $(tflint "${ARGS[@]}" 2>&1) 2> /dev/null || { - echo >&2 -e "\033[1;31m\nERROR in $path_uniq/:\033[0m" + echo >&2 -e "\033[1;33m\nTFLint in $path_uniq/:\033[0m" tflint "${ARGS[@]}" } From 71302a9a56ccd9fee424f4bb978dc4e2e1ac13f2 Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Mon, 8 Nov 2021 10:51:08 +0200 Subject: [PATCH 169/214] feat: Updated Docker image from Ubuntu to Alpine (#278) --- Dockerfile | 106 ++++++++++++++++++++++++----------------------------- 1 file changed, 48 insertions(+), 58 deletions(-) diff --git a/Dockerfile b/Dockerfile index 39a539cd2..d9141d104 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,23 +1,14 @@ -FROM ubuntu:20.04 as builder - -# Install general dependencies -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive apt install -y \ - # Needed for pre-commit in next build stage - git \ - libpcre2-8-0 \ - # Builder deps - unzip \ - software-properties-common \ - curl \ - python3 \ - python3-pip \ - # infracost deps - jq && \ +ARG TAG=3.9.7-alpine3.14 +FROM python:${TAG} as builder + +WORKDIR /bin_dir + +RUN apk add --no-cache \ + # Builder deps + curl \ + unzip && \ # Upgrade pip for be able get latest Checkov - python3 -m pip install --upgrade pip && \ - # Cleanup - rm -rf /var/lib/apt/lists/* + python3 -m pip install --upgrade pip ARG PRE_COMMIT_VERSION=${PRE_COMMIT_VERSION:-latest} ARG TERRAFORM_VERSION=${TERRAFORM_VERSION:-latest} @@ -27,21 +18,15 @@ RUN [ ${PRE_COMMIT_VERSION} = "latest" ] && pip3 install --no-cache-dir pre-comm || pip3 install --no-cache-dir pre-commit==${PRE_COMMIT_VERSION} # Install terraform because pre-commit needs it -RUN curl -fsSL https://apt.releases.hashicorp.com/gpg | apt-key add - && \ - apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main" && \ - apt update && \ - ( \ - [ "$TERRAFORM_VERSION" = "latest" ] && apt install -y terraform \ - || apt install -y terraform=${TERRAFORM_VERSION} \ - ) && \ - # Cleanup - rm -rf /var/lib/apt/lists/* +RUN if [ "${TERRAFORM_VERSION}" = "latest" ]; then \ + TERRAFORM_VERSION="$(curl -s https://api.github.com/repos/hashicorp/terraform/releases/latest | grep tag_name | grep -o -E -m 1 "[0-9.]+")" \ + ; fi && \ + curl -L "https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip" > terraform.zip && \ + unzip terraform.zip terraform && rm terraform.zip # # Install tools # -WORKDIR /bin_dir - ARG CHECKOV_VERSION=${CHECKOV_VERSION:-false} ARG INFRACOST_VERSION=${INFRACOST_VERSION:-false} ARG TERRAFORM_DOCS_VERSION=${TERRAFORM_DOCS_VERSION:-false} @@ -143,42 +128,47 @@ RUN . /.env && \ RUN . /.env && \ F=tools_versions_info && \ pre-commit --version >> $F && \ - terraform --version | head -n 1 >> $F && \ - (if [ "$CHECKOV_VERSION" != "false" ]; then echo "checkov $(checkov --version)" >> $F; else echo "checkov SKIPPED" >> $F ; fi) && \ - (if [ "$INFRACOST_VERSION" != "false" ]; then echo "$(./infracost --version)" >> $F; else echo "infracost SKIPPED" >> $F ; fi) && \ - (if [ "$TERRAFORM_DOCS_VERSION" != "false" ]; then ./terraform-docs --version >> $F; else echo "terraform-docs SKIPPED" >> $F; fi) && \ - (if [ "$TERRAGRUNT_VERSION" != "false" ]; then ./terragrunt --version >> $F; else echo "terragrunt SKIPPED" >> $F ; fi) && \ - (if [ "$TERRASCAN_VERSION" != "false" ]; then echo "terrascan $(./terrascan version)" >> $F; else echo "terrascan SKIPPED" >> $F ; fi) && \ - (if [ "$TFLINT_VERSION" != "false" ]; then ./tflint --version >> $F; else echo "tflint SKIPPED" >> $F ; fi) && \ - (if [ "$TFSEC_VERSION" != "false" ]; then echo "tfsec $(./tfsec --version)" >> $F; else echo "tfsec SKIPPED" >> $F ; fi) && \ - echo "\n\n" && cat $F && echo "\n\n" - -# based on debian:buster-slim -# https://github.com/docker-library/python/blob/master/3.9/buster/slim/Dockerfile -FROM python:3.9-slim-buster - -# Python 3.8 (ubuntu 20.04) -> Python3.9 hacks -COPY --from=builder /usr/local/lib/python3.8/dist-packages/ /usr/local/lib/python3.9/site-packages/ -COPY --from=builder /usr/lib/python3/dist-packages /usr/local/lib/python3.9/site-packages -RUN mkdir /usr/lib/python3 && \ - ln -s /usr/local/lib/python3.9/site-packages /usr/lib/python3/site-packages && \ - ln -s /usr/local/bin/python3 /usr/bin/python3 -# Copy binaries needed for pre-commit -COPY --from=builder /usr/lib/git-core/ /usr/lib/git-core/ -COPY --from=builder /usr/lib/x86_64-linux-gnu/libpcre2-8.so.0 /usr/lib/x86_64-linux-gnu/ + ./terraform --version | head -n 1 >> $F && \ + (if [ "$CHECKOV_VERSION" != "false" ]; then echo "checkov $(checkov --version)" >> $F; else echo "checkov SKIPPED" >> $F ; fi) && \ + (if [ "$INFRACOST_VERSION" != "false" ]; then echo "$(./infracost --version)" >> $F; else echo "infracost SKIPPED" >> $F ; fi) && \ + (if [ "$TERRAFORM_DOCS_VERSION" != "false" ]; then ./terraform-docs --version >> $F; else echo "terraform-docs SKIPPED" >> $F ; fi) && \ + (if [ "$TERRAGRUNT_VERSION" != "false" ]; then ./terragrunt --version >> $F; else echo "terragrunt SKIPPED" >> $F ; fi) && \ + (if [ "$TERRASCAN_VERSION" != "false" ]; then echo "terrascan $(./terrascan version)" >> $F; else echo "terrascan SKIPPED" >> $F ; fi) && \ + (if [ "$TFLINT_VERSION" != "false" ]; then ./tflint --version >> $F; else echo "tflint SKIPPED" >> $F ; fi) && \ + (if [ "$TFSEC_VERSION" != "false" ]; then echo "tfsec $(./tfsec --version)" >> $F; else echo "tfsec SKIPPED" >> $F ; fi) && \ + echo -e "\n\n" && cat $F && echo -e "\n\n" + + + +FROM python:${TAG} + +RUN apk add --no-cache \ + # pre-commit deps + git \ + # All hooks deps + bash + # Copy tools COPY --from=builder \ + # Needed for all hooks + /usr/local/bin/pre-commit \ + # Hooks and terraform binaries /bin_dir/ \ - /usr/bin/terraform \ /usr/local/bin/checkov* \ - /usr/local/bin/pre-commit \ - /usr/bin/git \ - /usr/bin/git-shell \ - /usr/bin/jq \ /usr/bin/ +# Copy pre-commit packages +COPY --from=builder /usr/local/lib/python3.9/site-packages/ /usr/local/lib/python3.9/site-packages/ # Copy terrascan policies COPY --from=builder /root/ /root/ +# Install hooks extra deps +RUN if [ "$(grep -o '^terraform-docs SKIPPED$' /usr/bin/tools_versions_info)" = "" ]; then \ + apk add --no-cache perl \ + ; fi && \ + if [ "$(grep -o '^infracost SKIPPED$' /usr/bin/tools_versions_info)" = "" ]; then \ + apk add --no-cache jq \ + ; fi + ENV PRE_COMMIT_COLOR=${PRE_COMMIT_COLOR:-always} ENV INFRACOST_API_KEY=${INFRACOST_API_KEY:-} From d4ae82fa95714afe0370e7967d7818612b85fafc Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Mon, 8 Nov 2021 09:53:57 +0100 Subject: [PATCH 170/214] Updated CHANGELOG --- CHANGELOG.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fd0963fa..b0194db85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ All notable changes to this project will be documented in this file. + +## [v1.56.0] - 2021-11-08 + +- feat: Updated Docker image from Ubuntu to Alpine ([#278](https://github.com/antonbabenko/pre-commit-terraform/issues/278)) +- chore: Updated messages shown in terraform_tflint hook ([#274](https://github.com/antonbabenko/pre-commit-terraform/issues/274)) + + ## [v1.55.0] - 2021-10-27 @@ -480,7 +487,8 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.55.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.56.0...HEAD +[v1.56.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.55.0...v1.56.0 [v1.55.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.54.0...v1.55.0 [v1.54.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.53.0...v1.54.0 [v1.53.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.52.0...v1.53.0 From af10d4f082b496a86f1c5cc80e6e1faaedc69c39 Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Mon, 8 Nov 2021 22:28:02 +0200 Subject: [PATCH 171/214] chore: Add deprecation notice to `terraform_docs_replace` (#280) --- README.md | 38 ++++++++++++++++++++---------------- pre_commit_hooks/__init__.py | 4 ++++ 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 24aaedb67..8a60c6200 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ If you are using `pre-commit-terraform` already or want to support its developme ## Table of content +* [Sponsors](#sponsors) +* [Table of content](#table-of-content) * [How to install](#how-to-install) * [1. Install dependencies](#1-install-dependencies) * [2. Install the pre-commit hook globally](#2-install-the-pre-commit-hook-globally) @@ -30,7 +32,7 @@ If you are using `pre-commit-terraform` already or want to support its developme * [checkov](#checkov) * [infracost_breakdown](#infracost_breakdown) * [terraform_docs](#terraform_docs) - * [terraform_docs_replace](#terraform_docs_replace) + * [terraform_docs_replace (deprecated)](#terraform_docs_replace-deprecated) * [terraform_fmt](#terraform_fmt) * [terraform_providers_lock](#terraform_providers_lock) * [terraform_tflint](#terraform_tflint) @@ -188,21 +190,21 @@ docker run --entrypoint cat pre-commit:latest /usr/bin/tools_versions_info There are several [pre-commit](https://pre-commit.com/) hooks to keep Terraform configurations (both `*.tf` and `*.tfvars`) and Terragrunt configurations (`*.hcl`) in a good shape: -| Hook name | Description | Dependencies
[Install instructions here](#1-install-dependencies) | -| ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | -| `checkov` | [checkov](https://github.com/bridgecrewio/checkov) static analysis of terraform templates to spot potential security issues. [Hook notes](#checkov) | `checkov`
Ubuntu deps: `python3`, `python3-pip` | -| `infracost_breakdown` | Check how much your infra costs with [infracost](https://github.com/infracost/infracost). [Hook notes](#infracost_breakdown) | `infracost`, `jq`, [Infracost API key](https://www.infracost.io/docs/#2-get-api-key) -| `terraform_docs_replace` | Runs `terraform-docs` and pipes the output directly to README.md | `python3`, `terraform-docs` | -| `terraform_docs_without_`
`aggregate_type_defaults` | Inserts input and output documentation into `README.md` without aggregate type defaults. Hook notes same as for [terraform_docs](#terraform_docs) | `terraform-docs` | -| `terraform_docs` | Inserts input and output documentation into `README.md`. Recommended. [Hook notes](#terraform_docs) | `terraform-docs` | -| `terraform_fmt` | Reformat all Terraform configuration files to a canonical format. [Hook notes](#terraform_fmt) | - | -| `terraform_providers_lock` | Updates provider signatures in [dependency lock files](https://www.terraform.io/docs/cli/commands/providers/lock.html). [Hook notes](#terraform_providers_lock) | - | -| `terraform_tflint` | Validates all Terraform configuration files with [TFLint](https://github.com/terraform-linters/tflint). [Available TFLint rules](https://github.com/terraform-linters/tflint/tree/master/docs/rules#rules). [Hook notes](#terraform_tflint). | `tflint` | -| `terraform_tfsec` | [TFSec](https://github.com/aquasecurity/tfsec) static analysis of terraform templates to spot potential security issues. [Hook notes](#terraform_tfsec) | `tfsec` | -| `terraform_validate` | Validates all Terraform configuration files. [Hook notes](#terraform_validate) | - | -| `terragrunt_fmt` | Reformat all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) to a canonical format. | `terragrunt` | -| `terragrunt_validate` | Validates all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) | `terragrunt` | -| `terrascan` | [terrascan](https://github.com/accurics/terrascan) Detect compliance and security violations. | `terrascan` | +| Hook name | Description | Dependencies
[Install instructions here](#1-install-dependencies) | +| ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | +| `checkov` | [checkov](https://github.com/bridgecrewio/checkov) static analysis of terraform templates to spot potential security issues. [Hook notes](#checkov) | `checkov`
Ubuntu deps: `python3`, `python3-pip` | +| `infracost_breakdown` | Check how much your infra costs with [infracost](https://github.com/infracost/infracost). [Hook notes](#infracost_breakdown) | `infracost`, `jq`, [Infracost API key](https://www.infracost.io/docs/#2-get-api-key) | +| `terraform_docs_replace` | Runs `terraform-docs` and pipes the output directly to README.md. **DEPRECATED**, see [#248](https://github.com/antonbabenko/pre-commit-terraform/issues/248). [Hook notes](#terraform_docs_replace-deprecated) | `python3`, `terraform-docs` | +| `terraform_docs_without_`
`aggregate_type_defaults` | Inserts input and output documentation into `README.md` without aggregate type defaults. Hook notes same as for [terraform_docs](#terraform_docs) | `terraform-docs` | +| `terraform_docs` | Inserts input and output documentation into `README.md`. Recommended. [Hook notes](#terraform_docs) | `terraform-docs` | +| `terraform_fmt` | Reformat all Terraform configuration files to a canonical format. [Hook notes](#terraform_fmt) | - | +| `terraform_providers_lock` | Updates provider signatures in [dependency lock files](https://www.terraform.io/docs/cli/commands/providers/lock.html). [Hook notes](#terraform_providers_lock) | - | +| `terraform_tflint` | Validates all Terraform configuration files with [TFLint](https://github.com/terraform-linters/tflint). [Available TFLint rules](https://github.com/terraform-linters/tflint/tree/master/docs/rules#rules). [Hook notes](#terraform_tflint). | `tflint` | +| `terraform_tfsec` | [TFSec](https://github.com/aquasecurity/tfsec) static analysis of terraform templates to spot potential security issues. [Hook notes](#terraform_tfsec) | `tfsec` | +| `terraform_validate` | Validates all Terraform configuration files. [Hook notes](#terraform_validate) | - | +| `terragrunt_fmt` | Reformat all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) to a canonical format. | `terragrunt` | +| `terragrunt_validate` | Validates all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) | `terragrunt` | +| `terrascan` | [terrascan](https://github.com/accurics/terrascan) Detect compliance and security violations. | `terrascan` | Check the [source file](https://github.com/antonbabenko/pre-commit-terraform/blob/master/.pre-commit-hooks.yaml) to know arguments used for each hook. @@ -346,7 +348,9 @@ Unlike most other hooks, this hook triggers once if there are any changed files - tfvars hcl --output-file terraform.tfvars.model . ``` -### terraform_docs_replace +### terraform_docs_replace (deprecated) + +**DEPRECATED**. Will be merged in [`terraform_docs`](#terraform_docs). See [#248](https://github.com/antonbabenko/pre-commit-terraform/issues/248) for details. `terraform_docs_replace` replaces the entire README.md rather than doing string replacement between markers. Put your additional documentation at the top of your `main.tf` for it to be pulled in. The optional `--dest` argument lets you change the filename that gets created/modified. diff --git a/pre_commit_hooks/__init__.py b/pre_commit_hooks/__init__.py index e69de29bb..ff01067a9 100644 --- a/pre_commit_hooks/__init__.py +++ b/pre_commit_hooks/__init__.py @@ -0,0 +1,4 @@ +print( + '`terraform_docs_replace` hook is DEPRECATED.' + 'For details, see https://github.com/antonbabenko/pre-commit-terraform/issues/248' +) From feabecc9b3461fff58e0a29541854d3e7a5402c0 Mon Sep 17 00:00:00 2001 From: Milos Jajac Date: Wed, 17 Nov 2021 19:16:38 +0100 Subject: [PATCH 172/214] fix: typo in arg name for terraform-docs (#283) --- README.md | 2 +- terraform_docs.sh | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 8a60c6200..1720626bc 100644 --- a/README.md +++ b/README.md @@ -329,7 +329,7 @@ Unlike most other hooks, this hook triggers once if there are any changed files - id: terraform_docs args: - --hook-config=--path-to-file=README.md # Valid UNIX path. I.e. ../TFDOC.md or docs/README.md etc. - - --hook-config=--add-to-exiting-file=true # Boolean. true or false + - --hook-config=--add-to-existing-file=true # Boolean. true or false - --hook-config=--create-file-if-not-exist=true # Boolean. true or false ``` diff --git a/terraform_docs.sh b/terraform_docs.sh index 1152cb110..3b04afe24 100755 --- a/terraform_docs.sh +++ b/terraform_docs.sh @@ -119,7 +119,7 @@ terraform_docs() { # Get hook settings # local text_file="README.md" - local add_to_exiting=false + local add_to_existing=false local create_if_not_exist=false configs=($hook_config) @@ -132,8 +132,8 @@ terraform_docs() { --path-to-file) text_file=$value ;; - --add-to-exiting-file) - add_to_exiting=$value + --add-to-existing-file) + add_to_existing=$value ;; --create-file-if-not-exist) create_if_not_exist=$value @@ -171,10 +171,10 @@ terraform_docs() { [[ ! -f "$text_file" ]] && popd > /dev/null && continue # - # If `--add-to-exiting-file=true` set, check is in file exist "hook markers", + # If `--add-to-existing-file=true` set, check is in file exist "hook markers", # and if not - append "hook markers" to the end of file. # - if $add_to_exiting; then + if $add_to_existing; then HAVE_MARKER=$(grep -o '' "$text_file" || exit 0) if [ ! "$HAVE_MARKER" ]; then From c5bcad45bba430466b4171978a9c3d58562d2bfb Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Wed, 17 Nov 2021 19:17:50 +0100 Subject: [PATCH 173/214] Updated CHANGELOG --- CHANGELOG.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0194db85..a7aad7c3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ All notable changes to this project will be documented in this file. + +## [v1.57.0] - 2021-11-17 + +- fix: typo in arg name for terraform-docs ([#283](https://github.com/antonbabenko/pre-commit-terraform/issues/283)) +- chore: Add deprecation notice to `terraform_docs_replace` ([#280](https://github.com/antonbabenko/pre-commit-terraform/issues/280)) + + ## [v1.56.0] - 2021-11-08 @@ -487,7 +494,8 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.56.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.57.0...HEAD +[v1.57.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.56.0...v1.57.0 [v1.56.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.55.0...v1.56.0 [v1.55.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.54.0...v1.55.0 [v1.54.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.53.0...v1.54.0 From 3b4be67fdb4476f7abd36feeba074ab67dc1cad5 Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Wed, 17 Nov 2021 22:21:09 +0200 Subject: [PATCH 174/214] chore: Fix master merge to working branch on pre-commit autofixes (#286) --- .github/workflows/pre-commit.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index 9672e077f..07241d1a0 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -25,6 +25,7 @@ jobs: - uses: actions/checkout@v2 with: fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} - uses: actions/setup-python@v2 with: From 003cd1da3cce54451aff474420d3a004ec5364f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Birger=20J=2E=20Nord=C3=B8lum?= Date: Sat, 20 Nov 2021 11:28:03 +0100 Subject: [PATCH 175/214] chore: Publish container image on release (#285) --- .github/workflows/build-image.yaml | 39 ++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/workflows/build-image.yaml diff --git a/.github/workflows/build-image.yaml b/.github/workflows/build-image.yaml new file mode 100644 index 000000000..ddfe09469 --- /dev/null +++ b/.github/workflows/build-image.yaml @@ -0,0 +1,39 @@ +--- +name: publish container image + +on: + push: + branches: + - master + release: + types: + - created + workflow_dispatch: + +jobs: + docker: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + - name: Set up QEMU + uses: docker/setup-qemu-action@v1 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - name: Login to GitHub Container Registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Set tag for image + run: | + echo IMAGE_TAG=$([ ${{ github.ref_type }} == 'tag' ] && echo ${{ github.ref_name }} || echo 'latest') >> $GITHUB_ENV + - name: Build and Push + uses: docker/build-push-action@v2 + with: + context: . + platforms: linux/amd64 + push: true + tags: | + ghcr.io/${{ github.repository }}:${{ env.IMAGE_TAG }} From 6a064b19e4730a42cc19b2793f617f97a0d2e262 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Sat, 20 Nov 2021 11:28:49 +0100 Subject: [PATCH 176/214] Updated CHANGELOG --- CHANGELOG.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7aad7c3f..7ddf4df23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ All notable changes to this project will be documented in this file. + +## [v1.58.0] - 2021-11-20 + +- chore: Publish container image on release ([#285](https://github.com/antonbabenko/pre-commit-terraform/issues/285)) +- chore: Fix master merge to working branch on pre-commit autofixes ([#286](https://github.com/antonbabenko/pre-commit-terraform/issues/286)) + + ## [v1.57.0] - 2021-11-17 @@ -494,7 +501,8 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.57.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.58.0...HEAD +[v1.58.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.57.0...v1.58.0 [v1.57.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.56.0...v1.57.0 [v1.56.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.55.0...v1.56.0 [v1.55.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.54.0...v1.55.0 From 4543f10a8ea1b9c522b931b1c2e3331b832ecfc0 Mon Sep 17 00:00:00 2001 From: sg70 Date: Mon, 6 Dec 2021 15:55:06 +0100 Subject: [PATCH 177/214] fix: Fixed docker build (#288) --- Dockerfile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index d9141d104..d643404a0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,8 @@ ARG TAG=3.9.7-alpine3.14 FROM python:${TAG} as builder +ENV PYTHONUNBUFFERED 1 + WORKDIR /bin_dir RUN apk add --no-cache \ @@ -57,8 +59,10 @@ RUN if [ "$INSTALL_ALL" != "false" ]; then \ RUN . /.env && \ if [ "$CHECKOV_VERSION" != "false" ]; then \ ( \ + apk add --no-cache gcc libffi-dev musl-dev; \ [ "$CHECKOV_VERSION" = "latest" ] && pip3 install --no-cache-dir checkov \ - || pip3 install --no-cache-dir checkov==${CHECKOV_VERSION} \ + || pip3 install --no-cache-dir checkov==${CHECKOV_VERSION}; \ + apk del gcc libffi-dev musl-dev \ ) \ ; fi From 31e7ccf1b76b477441136eef21f24b9f91695dda Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Mon, 6 Dec 2021 15:57:13 +0100 Subject: [PATCH 178/214] Updated CHANGELOG --- CHANGELOG.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ddf4df23..00fcabd3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ All notable changes to this project will be documented in this file. + +## [v1.59.0] - 2021-12-06 + +- fix: Fixed docker build ([#288](https://github.com/antonbabenko/pre-commit-terraform/issues/288)) + + ## [v1.58.0] - 2021-11-20 @@ -501,7 +507,8 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.58.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.59.0...HEAD +[v1.59.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.58.0...v1.59.0 [v1.58.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.57.0...v1.58.0 [v1.57.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.56.0...v1.57.0 [v1.56.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.55.0...v1.56.0 From 01d262caaae1fe0a886e5e8e16a58a1bffd5713c Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Wed, 8 Dec 2021 21:03:04 +0200 Subject: [PATCH 179/214] fix: pre-build docker image (#292) --- .github/workflows/build-image.yaml | 2 ++ Dockerfile | 2 -- README.md | 35 +++++++++++++++++++++++------- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build-image.yaml b/.github/workflows/build-image.yaml index ddfe09469..d4b630d80 100644 --- a/.github/workflows/build-image.yaml +++ b/.github/workflows/build-image.yaml @@ -33,6 +33,8 @@ jobs: uses: docker/build-push-action@v2 with: context: . + build-args: | + INSTALL_ALL=true platforms: linux/amd64 push: true tags: | diff --git a/Dockerfile b/Dockerfile index d643404a0..9266eb346 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,6 @@ ARG TAG=3.9.7-alpine3.14 FROM python:${TAG} as builder -ENV PYTHONUNBUFFERED 1 - WORKDIR /bin_dir RUN apk add --no-cache \ diff --git a/README.md b/README.md index 1720626bc..2bc67f054 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ Want to contribute? Check [open issues](https://github.com/antonbabenko/pre-comm ## Sponsors + +
env0 @@ -14,10 +16,13 @@ Automated provisioning of Terraform workflows and Infrastructure as Code.
infracost + + Cloud cost estimates for Terraform. If you are using `pre-commit-terraform` already or want to support its development and [many other open-source projects](https://github.com/antonbabenko/terraform-aws-devops), please become a [GitHub Sponsor](https://github.com/sponsors/antonbabenko)! + ## Table of content * [Sponsors](#sponsors) @@ -59,19 +64,30 @@ If you are using `pre-commit-terraform` already or want to support its developme
Docker
-When `--build-arg` is not specified, the latest version of `pre-commit` and `terraform` will be installed. +**Pull docker image with all hooks**: + +```bash +TAG=latest +docker pull ghcr.io/antonbabenko/pre-commit-terraform:$TAG +``` + +All available tags [here](https://github.com/antonbabenko/pre-commit-terraform/pkgs/container/pre-commit-terraform/versions). + +**Build from scratch**: + +When `--build-arg` is not specified, the latest version of `pre-commit` and `terraform` will be only installed. ```bash git clone git@github.com:antonbabenko/pre-commit-terraform.git cd pre-commit-terraform # Install the latest versions of all the tools -docker build -t pre-commit --build-arg INSTALL_ALL=true . +docker build -t pre-commit-terraform --build-arg INSTALL_ALL=true . ``` To install a specific version of individual tools, define it using `--build-arg` arguments or set it to `latest`: ```bash -docker build -t pre-commit \ +docker build -t pre-commit-terraform \ --build-arg PRE_COMMIT_VERSION=latest \ --build-arg TERRAFORM_VERSION=latest \ --build-arg CHECKOV_VERSION=2.0.405 \ @@ -174,15 +190,18 @@ Execute this command to run `pre-commit` on all files in the repository (not onl pre-commit run -a ``` -Or, using Docker: +Or, using Docker ([available tags](https://github.com/antonbabenko/pre-commit-terraform/pkgs/container/pre-commit-terraform/versions)): ```bash -docker run -v $(pwd):/lint -w /lint pre-commit run -a +TAG=latest +docker run -v $(pwd):/lint -w /lint ghcr.io/antonbabenko/pre-commit-terraform:$TAG run -a ``` Execute this command to list the versions of the tools in Docker: + ```bash -docker run --entrypoint cat pre-commit:latest /usr/bin/tools_versions_info +TAG=latest +docker run --entrypoint cat ghcr.io/antonbabenko/pre-commit-terraform:$TAG /usr/bin/tools_versions_info ``` ## Available Hooks @@ -321,7 +340,7 @@ Unlike most other hooks, this hook triggers once if there are any changed files 2. It is possible to pass additional arguments to shell scripts when using `terraform_docs` and `terraform_docs_without_aggregate_type_defaults`. 3. It is possible to automatically: - * create documentation file + * create a documentation file * extend existing documentation file by appending markers to the end of the file (see item 1 above) * use different filename for the documentation (default is `README.md`) @@ -333,7 +352,7 @@ Unlike most other hooks, this hook triggers once if there are any changed files - --hook-config=--create-file-if-not-exist=true # Boolean. true or false ``` -4. You can provide [any configuration available in `terraform-docs`](https://terraform-docs.io/user-guide/configuration/) as argument to `terraform_doc` hook, for example: +4. You can provide [any configuration available in `terraform-docs`](https://terraform-docs.io/user-guide/configuration/) as an argument to `terraform_doc` hook, for example: ```yaml - id: terraform_docs From 141f815e810dfdd2dc3c5d0c605e883f4e5e14de Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Wed, 8 Dec 2021 20:03:43 +0100 Subject: [PATCH 180/214] Updated CHANGELOG --- CHANGELOG.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 00fcabd3b..f0e40cd05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ All notable changes to this project will be documented in this file. + +## [v1.60.0] - 2021-12-08 + +- fix: pre-build docker image ([#292](https://github.com/antonbabenko/pre-commit-terraform/issues/292)) + + ## [v1.59.0] - 2021-12-06 @@ -507,7 +513,8 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.59.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.60.0...HEAD +[v1.60.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.59.0...v1.60.0 [v1.59.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.58.0...v1.59.0 [v1.58.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.57.0...v1.58.0 [v1.57.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.56.0...v1.57.0 From 7c6ad7cfb40d92e8f49cb0a3dd412c7509d5aeda Mon Sep 17 00:00:00 2001 From: Bruno Ferreira Date: Wed, 8 Dec 2021 21:06:30 +0000 Subject: [PATCH 181/214] fix: analyse all folders with tflint and don't stop on first execution (#289) --- terraform_tflint.sh | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/terraform_tflint.sh b/terraform_tflint.sh index af2040877..66cf1fc68 100755 --- a/terraform_tflint.sh +++ b/terraform_tflint.sh @@ -58,20 +58,24 @@ tflint_() { ((index += 1)) done - + set +e + tflint_final_exit_code=0 for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do path_uniq="${path_uniq//__REPLACED__SPACE__/ }" - pushd "$path_uniq" > /dev/null # Print checked PATH **only** if TFLint have any messages # shellcheck disable=SC2091 # Suppress error output - $(tflint "${ARGS[@]}" 2>&1) 2> /dev/null || { + $(tflint "${ARGS[@]}" $path_uniq 2>&1) 2> /dev/null || { echo >&2 -e "\033[1;33m\nTFLint in $path_uniq/:\033[0m" - tflint "${ARGS[@]}" + tflint "${ARGS[@]}" $path_uniq } - - popd > /dev/null + local exit_code=$? + if [ $exit_code != 0 ]; then + tflint_final_exit_code=$exit_code + fi done + set -e + exit $tflint_final_exit_code } # global arrays From 45575c3a434a8a76c1f62de824e5156edf887086 Mon Sep 17 00:00:00 2001 From: Maxime Brunet Date: Sat, 11 Dec 2021 05:33:22 -0800 Subject: [PATCH 182/214] feat: Pass custom arguments to terraform init in `terraform_validate` hook (#293) --- README.md | 10 +++++++++- terraform_validate.sh | 10 ++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2bc67f054..45f8b3049 100644 --- a/README.md +++ b/README.md @@ -526,7 +526,15 @@ Example: - --envs=AWS_SECRET_ACCESS_KEY="asecretkey" ``` -3. It may happen that Terraform working directory (`.terraform`) already exists but not in the best condition (eg, not initialized modules, wrong version of Terraform, etc.). To solve this problem, you can find and delete all `.terraform` directories in your repository: +3. `terraform_validate` also supports passing custom arguments to its `terraform init`: + + ```yaml + - id: terraform_validate + args: + - --init-args=-lockfile=readonly + ``` + +4. It may happen that Terraform working directory (`.terraform`) already exists but not in the best condition (eg, not initialized modules, wrong version of Terraform, etc.). To solve this problem, you can find and delete all `.terraform` directories in your repository: ```bash echo " diff --git a/terraform_validate.sh b/terraform_validate.sh index 236b35138..ea23dac7f 100755 --- a/terraform_validate.sh +++ b/terraform_validate.sh @@ -30,7 +30,7 @@ initialize_() { parse_cmdline_() { declare argv - argv=$(getopt -o e:a: --long envs:,args: -- "$@") || return + argv=$(getopt -o e:i:a: --long envs:,init-args:,args: -- "$@") || return eval "set -- $argv" for argv; do @@ -40,6 +40,11 @@ parse_cmdline_() { ARGS+=("$1") shift ;; + -i | --init-args) + shift + INIT_ARGS+=("$1") + shift + ;; -e | --envs) shift ENVS+=("$1") @@ -87,7 +92,7 @@ terraform_validate_() { if [[ ! -d .terraform ]]; then set +e - init_output=$(terraform init -backend=false 2>&1) + init_output=$(terraform init -backend=false "${INIT_ARGS[@]}" 2>&1) init_code=$? set -e @@ -123,6 +128,7 @@ terraform_validate_() { # global arrays declare -a ARGS +declare -a INIT_ARGS declare -a ENVS declare -a FILES From 00a5ef032581e4334f3cd3b31bfc9cc93c4b4870 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Sat, 11 Dec 2021 14:34:55 +0100 Subject: [PATCH 183/214] Updated CHANGELOG --- CHANGELOG.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0e40cd05..d73adf8f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ All notable changes to this project will be documented in this file. + +## [v1.61.0] - 2021-12-11 + +- feat: Pass custom arguments to terraform init in `terraform_validate` hook ([#293](https://github.com/antonbabenko/pre-commit-terraform/issues/293)) +- fix: analyse all folders with tflint and don't stop on first execution ([#289](https://github.com/antonbabenko/pre-commit-terraform/issues/289)) + + ## [v1.60.0] - 2021-12-08 @@ -513,7 +520,8 @@ https://github.com/antonbabenko/pre-commit-terraform/commit/35e0356188b64a4c5af9 - Initial commit -[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.60.0...HEAD +[Unreleased]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.61.0...HEAD +[v1.61.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.60.0...v1.61.0 [v1.60.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.59.0...v1.60.0 [v1.59.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.58.0...v1.59.0 [v1.58.0]: https://github.com/antonbabenko/pre-commit-terraform/compare/v1.57.0...v1.58.0 From 1bcca44d1677128c23d505be644f1d16c320eb4c Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Sun, 12 Dec 2021 20:28:30 +0100 Subject: [PATCH 184/214] feat: Added semantic release (#296) --- .chglog/CHANGELOG.tpl.md | 111 ---------------------------------- .chglog/config.yml | 10 --- .github/workflows/release.yml | 32 ++++++++++ .releaserc.json | 36 +++++++++++ Makefile | 7 --- 5 files changed, 68 insertions(+), 128 deletions(-) delete mode 100644 .chglog/CHANGELOG.tpl.md delete mode 100644 .chglog/config.yml create mode 100644 .github/workflows/release.yml create mode 100644 .releaserc.json delete mode 100644 Makefile diff --git a/.chglog/CHANGELOG.tpl.md b/.chglog/CHANGELOG.tpl.md deleted file mode 100644 index 687d70232..000000000 --- a/.chglog/CHANGELOG.tpl.md +++ /dev/null @@ -1,111 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. - -{{ if .Versions -}} - -## [Unreleased] -{{ if .Unreleased.CommitGroups -}} -{{ range .Unreleased.CommitGroups -}} -### {{ .Title }} -{{ range .Commits -}} -{{/* SKIPPING RULES - START */ -}} -{{- if not (hasPrefix .Subject "Updated CHANGELOG") -}} -{{- if not (contains .Subject "[ci skip]") -}} -{{- if not (contains .Subject "[skip ci]") -}} -{{- if not (hasPrefix .Subject "Merge pull request ") -}} -{{- if not (hasPrefix .Subject "Added CHANGELOG") -}} -{{- /* SKIPPING RULES - END */ -}} -- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} -{{/* SKIPPING RULES - START */ -}} -{{ end -}} -{{ end -}} -{{ end -}} -{{ end -}} -{{ end -}} -{{/* SKIPPING RULES - END */ -}} -{{ end }} -{{ end -}} -{{ else }} -{{ range .Unreleased.Commits -}} -{{/* SKIPPING RULES - START */ -}} -{{- if not (hasPrefix .Subject "Updated CHANGELOG") -}} -{{- if not (contains .Subject "[ci skip]") -}} -{{- if not (contains .Subject "[skip ci]") -}} -{{- if not (hasPrefix .Subject "Merge pull request ") -}} -{{- if not (hasPrefix .Subject "Added CHANGELOG") -}} -{{- /* SKIPPING RULES - END */ -}} -- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} -{{/* SKIPPING RULES - START */ -}} -{{ end -}} -{{ end -}} -{{ end -}} -{{ end -}} -{{ end -}} -{{/* SKIPPING RULES - END */ -}} -{{ end }} -{{ end -}} -{{ end -}} - -{{ range .Versions }} - -## {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }} -{{ if .CommitGroups -}} -{{ range .CommitGroups -}} -### {{ .Title }} -{{ range .Commits -}} -{{/* SKIPPING RULES - START */ -}} -{{- if not (hasPrefix .Subject "Updated CHANGELOG") -}} -{{- if not (contains .Subject "[ci skip]") -}} -{{- if not (contains .Subject "[skip ci]") -}} -{{- if not (hasPrefix .Subject "Merge pull request ") -}} -{{- if not (hasPrefix .Subject "Added CHANGELOG") -}} -{{- /* SKIPPING RULES - END */ -}} -- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} -{{/* SKIPPING RULES - START */ -}} -{{ end -}} -{{ end -}} -{{ end -}} -{{ end -}} -{{ end -}} -{{/* SKIPPING RULES - END */ -}} -{{ end }} -{{ end -}} -{{ else }} -{{ range .Commits -}} -{{/* SKIPPING RULES - START */ -}} -{{- if not (hasPrefix .Subject "Updated CHANGELOG") -}} -{{- if not (contains .Subject "[ci skip]") -}} -{{- if not (contains .Subject "[skip ci]") -}} -{{- if not (hasPrefix .Subject "Merge pull request ") -}} -{{- if not (hasPrefix .Subject "Added CHANGELOG") -}} -{{- /* SKIPPING RULES - END */ -}} -- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} -{{/* SKIPPING RULES - START */ -}} -{{ end -}} -{{ end -}} -{{ end -}} -{{ end -}} -{{ end -}} -{{/* SKIPPING RULES - END */ -}} -{{ end }} -{{ end -}} - -{{- if .NoteGroups -}} -{{ range .NoteGroups -}} -### {{ .Title }} -{{ range .Notes }} -{{ .Body }} -{{ end }} -{{ end -}} -{{ end -}} -{{ end -}} - -{{- if .Versions }} -[Unreleased]: {{ .Info.RepositoryURL }}/compare/{{ $latest := index .Versions 0 }}{{ $latest.Tag.Name }}...HEAD -{{ range .Versions -}} -{{ if .Tag.Previous -}} -[{{ .Tag.Name }}]: {{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }} -{{ end -}} -{{ end -}} -{{ end -}} diff --git a/.chglog/config.yml b/.chglog/config.yml deleted file mode 100644 index 3529dbcd6..000000000 --- a/.chglog/config.yml +++ /dev/null @@ -1,10 +0,0 @@ -style: github -template: CHANGELOG.tpl.md -info: - title: CHANGELOG - repository_url: https://github.com/antonbabenko/pre-commit-terraform -options: - header: - pattern: "^(.*)$" - pattern_maps: - - Subject diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..58cdb7207 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,32 @@ +name: Release + +on: + workflow_dispatch: + push: + branches: + - main + - master + paths: + - '**/*.py' + - '**/*.sh' + +jobs: + release: + name: Release + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + persist-credentials: false + fetch-depth: 0 + + - name: Release + uses: cycjimmy/semantic-release-action@v2 + with: + semantic_version: 18.0.0 + extra_plugins: | + @semantic-release/changelog@6.0.0 + @semantic-release/git@10.0.0 + env: + GITHUB_TOKEN: ${{ secrets.SEMANTIC_RELEASE_TOKEN }} diff --git a/.releaserc.json b/.releaserc.json new file mode 100644 index 000000000..6e39031cf --- /dev/null +++ b/.releaserc.json @@ -0,0 +1,36 @@ +{ + "branches": [ + "main", + "master" + ], + "ci": false, + "plugins": [ + "@semantic-release/commit-analyzer", + "@semantic-release/release-notes-generator", + [ + "@semantic-release/github", + { + "successComment": + "This ${issue.pull_request ? 'PR is included' : 'issue has been resolved'} in version ${nextRelease.version} :tada:", + "labels": false, + "releasedLabels": false + } + ], + [ + "@semantic-release/changelog", + { + "changelogFile": "CHANGELOG.md", + "changelogTitle": "# Changelog\n\nAll notable changes to this project will be documented in this file." + } + ], + [ + "@semantic-release/git", + { + "assets": [ + "CHANGELOG.md" + ], + "message": "chore(release): version ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" + } + ] + ] +} diff --git a/Makefile b/Makefile deleted file mode 100644 index 558dac5a6..000000000 --- a/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -.PHONY: changelog release - -changelog: - git-chglog -o CHANGELOG.md --next-tag `semtag final -s minor -o` - -release: - semtag final -s minor From e1bccb7585af9fc64c7eeb8af04694cfc700cea0 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Sun, 12 Dec 2021 19:29:33 +0000 Subject: [PATCH 185/214] chore(release): version 1.62.0 [skip ci] # [1.62.0](https://github.com/antonbabenko/pre-commit-terraform/compare/v1.61.0...v1.62.0) (2021-12-12) ### Features * Added semantic release ([#296](https://github.com/antonbabenko/pre-commit-terraform/issues/296)) ([1bcca44](https://github.com/antonbabenko/pre-commit-terraform/commit/1bcca44d1677128c23d505be644f1d16c320eb4c)) --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d73adf8f3..15b767111 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +# [1.62.0](https://github.com/antonbabenko/pre-commit-terraform/compare/v1.61.0...v1.62.0) (2021-12-12) + + +### Features + +* Added semantic release ([#296](https://github.com/antonbabenko/pre-commit-terraform/issues/296)) ([1bcca44](https://github.com/antonbabenko/pre-commit-terraform/commit/1bcca44d1677128c23d505be644f1d16c320eb4c)) + # Change Log All notable changes to this project will be documented in this file. From 8c063c782462c2f8638f04d6283882a128a445fd Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Sun, 12 Dec 2021 21:08:14 +0100 Subject: [PATCH 186/214] chore: Validate PR title (#297) --- .github/semantic.yml | 17 +++++++++++ .github/workflows/pr-title.yml | 56 ++++++++++++++++++++++++++++++++++ .github/workflows/release.yml | 2 +- 3 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 .github/semantic.yml create mode 100644 .github/workflows/pr-title.yml diff --git a/.github/semantic.yml b/.github/semantic.yml new file mode 100644 index 000000000..4eae01a28 --- /dev/null +++ b/.github/semantic.yml @@ -0,0 +1,17 @@ +# Disable validation, and skip status check creation +enabled: true +# Always validate the PR title, and ignore the commits +titleOnly: true +# Always validate all commits, and ignore the PR title +commitsOnly: false +# Always validate the PR title AND all the commits +titleAndCommits: false +# By default types specified in commitizen/conventional-commit-types is used. +# See: https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json +# You can override the valid types +types: + - feat + - fix + - docs + - ci + - chore diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml new file mode 100644 index 000000000..48116c3e5 --- /dev/null +++ b/.github/workflows/pr-title.yml @@ -0,0 +1,56 @@ +name: "Validate PR title" + +on: + pull_request_target: + types: + - opened + - edited + - synchronize + +jobs: + main: + name: Validate PR title + runs-on: ubuntu-latest + steps: + # Please look up the latest version from + # https://github.com/amannn/action-semantic-pull-request/releases + - uses: amannn/action-semantic-pull-request@v3.4.6 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + # Configure which types are allowed. + # Default: https://github.com/commitizen/conventional-commit-types + types: | + fix + feat + docs + ci + chore + # Configure which scopes are allowed. + scopes: | + core + ui + # Configure that a scope must always be provided. + requireScope: false + # Configure additional validation for the subject based on a regex. + # This example ensures the subject starts with an uppercase character. + subjectPattern: ^[A-Z].+$ + # If `subjectPattern` is configured, you can use this property to override + # the default error message that is shown when the pattern doesn't match. + # The variables `subject` and `title` can be used within the message. + subjectPatternError: | + The subject "{subject}" found in the pull request title "{title}" + didn't match the configured pattern. Please ensure that the subject + starts with an uppercase character. + # For work-in-progress PRs you can typically use draft pull requests + # from Github. However, private repositories on the free plan don't have + # this option and therefore this action allows you to opt-in to using the + # special "[WIP]" prefix to indicate this state. This will avoid the + # validation of the PR title and the pull request checks remain pending. + # Note that a second check will be reported if this is enabled. + wip: true + # When using "Squash and merge" on a PR with only one commit, GitHub + # will suggest using that commit message instead of the PR title for the + # merge commit, and it's easy to commit this by mistake. Enable this option + # to also validate the commit message for one commit PRs. + validateSingleCommit: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 58cdb7207..fbcca3d27 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,4 +29,4 @@ jobs: @semantic-release/changelog@6.0.0 @semantic-release/git@10.0.0 env: - GITHUB_TOKEN: ${{ secrets.SEMANTIC_RELEASE_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From b8f90bd057594961b2f53d030294420dd781c0bb Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Sun, 12 Dec 2021 21:41:23 +0100 Subject: [PATCH 187/214] chore: Updated validation PR title types (#298) --- .github/semantic.yml | 17 ----------------- .github/workflows/pr-title.yml | 7 ++----- 2 files changed, 2 insertions(+), 22 deletions(-) delete mode 100644 .github/semantic.yml diff --git a/.github/semantic.yml b/.github/semantic.yml deleted file mode 100644 index 4eae01a28..000000000 --- a/.github/semantic.yml +++ /dev/null @@ -1,17 +0,0 @@ -# Disable validation, and skip status check creation -enabled: true -# Always validate the PR title, and ignore the commits -titleOnly: true -# Always validate all commits, and ignore the PR title -commitsOnly: false -# Always validate the PR title AND all the commits -titleAndCommits: false -# By default types specified in commitizen/conventional-commit-types is used. -# See: https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json -# You can override the valid types -types: - - feat - - fix - - docs - - ci - - chore diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml index 48116c3e5..7f5aec08e 100644 --- a/.github/workflows/pr-title.yml +++ b/.github/workflows/pr-title.yml @@ -26,10 +26,7 @@ jobs: docs ci chore - # Configure which scopes are allowed. - scopes: | - core - ui + BREAKING CHANGE # Configure that a scope must always be provided. requireScope: false # Configure additional validation for the subject based on a regex. @@ -53,4 +50,4 @@ jobs: # will suggest using that commit message instead of the PR title for the # merge commit, and it's easy to commit this by mistake. Enable this option # to also validate the commit message for one commit PRs. - validateSingleCommit: true + validateSingleCommit: false From 21185f6db4c4ebfb1998bc5aab96585eb8b60e82 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Mon, 13 Dec 2021 10:51:03 +0100 Subject: [PATCH 188/214] chore: Fixed allowed types for PR titles --- .github/workflows/pr-title.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml index 7f5aec08e..b89795d72 100644 --- a/.github/workflows/pr-title.yml +++ b/.github/workflows/pr-title.yml @@ -26,7 +26,6 @@ jobs: docs ci chore - BREAKING CHANGE # Configure that a scope must always be provided. requireScope: false # Configure additional validation for the subject based on a regex. From 59da4b81d61bb512fe6867eb9da713ed3b2a3c57 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Mon, 13 Dec 2021 10:58:27 +0100 Subject: [PATCH 189/214] chore: Publish container image only after the release --- .github/workflows/build-image.yaml | 8 ++------ .github/workflows/release.yml | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-image.yaml b/.github/workflows/build-image.yaml index d4b630d80..c096a7695 100644 --- a/.github/workflows/build-image.yaml +++ b/.github/workflows/build-image.yaml @@ -1,14 +1,10 @@ ---- -name: publish container image +name: Publish container image on: - push: - branches: - - master + workflow_dispatch: release: types: - created - workflow_dispatch: jobs: docker: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fbcca3d27..e8ac4e27d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,11 +4,11 @@ on: workflow_dispatch: push: branches: - - main - master paths: - '**/*.py' - '**/*.sh' + - 'Dockerfile' jobs: release: From 93029dcfcf6b9b121c24573f3e647d9fde255486 Mon Sep 17 00:00:00 2001 From: Maxime Brunet Date: Sat, 18 Dec 2021 10:07:36 -0800 Subject: [PATCH 190/214] fix(terraform_tflint): Restore current working directory behavior (#302) --- terraform_tflint.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/terraform_tflint.sh b/terraform_tflint.sh index 66cf1fc68..87c120383 100755 --- a/terraform_tflint.sh +++ b/terraform_tflint.sh @@ -62,17 +62,20 @@ tflint_() { tflint_final_exit_code=0 for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do path_uniq="${path_uniq//__REPLACED__SPACE__/ }" + pushd "$path_uniq" > /dev/null # Print checked PATH **only** if TFLint have any messages # shellcheck disable=SC2091 # Suppress error output - $(tflint "${ARGS[@]}" $path_uniq 2>&1) 2> /dev/null || { + $(tflint "${ARGS[@]}" 2>&1) 2> /dev/null || { echo >&2 -e "\033[1;33m\nTFLint in $path_uniq/:\033[0m" - tflint "${ARGS[@]}" $path_uniq + tflint "${ARGS[@]}" } local exit_code=$? if [ $exit_code != 0 ]; then tflint_final_exit_code=$exit_code fi + + popd > /dev/null done set -e exit $tflint_final_exit_code From 77bd66192f90511f6d859c93c716ed0ddc3ff356 Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Sat, 18 Dec 2021 22:15:00 +0100 Subject: [PATCH 191/214] chore: Use valid token for the Release GHA --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e8ac4e27d..7633f477f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,4 +29,4 @@ jobs: @semantic-release/changelog@6.0.0 @semantic-release/git@10.0.0 env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.SEMANTIC_RELEASE_TOKEN }} From d826474a12aa312152f01ae97ab5e12881a989fb Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Sat, 18 Dec 2021 21:17:33 +0000 Subject: [PATCH 192/214] chore(release): version 1.62.1 [skip ci] ## [1.62.1](https://github.com/antonbabenko/pre-commit-terraform/compare/v1.62.0...v1.62.1) (2021-12-18) ### Bug Fixes * **terraform_tflint:** Restore current working directory behavior ([#302](https://github.com/antonbabenko/pre-commit-terraform/issues/302)) ([93029dc](https://github.com/antonbabenko/pre-commit-terraform/commit/93029dcfcf6b9b121c24573f3e647d9fde255486)) --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15b767111..fccfbc7fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. +## [1.62.1](https://github.com/antonbabenko/pre-commit-terraform/compare/v1.62.0...v1.62.1) (2021-12-18) + + +### Bug Fixes + +* **terraform_tflint:** Restore current working directory behavior ([#302](https://github.com/antonbabenko/pre-commit-terraform/issues/302)) ([93029dc](https://github.com/antonbabenko/pre-commit-terraform/commit/93029dcfcf6b9b121c24573f3e647d9fde255486)) + # [1.62.0](https://github.com/antonbabenko/pre-commit-terraform/compare/v1.61.0...v1.62.0) (2021-12-12) From b431a43ffa6cd13156485ef853c967856e9572ef Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Tue, 21 Dec 2021 21:18:52 +0200 Subject: [PATCH 193/214] fix: Properly exclude .terraform directory with checkov hook (#306) --- .pre-commit-hooks.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 08e65ba27..f923dafb1 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -100,7 +100,7 @@ pass_filenames: false always_run: false files: \.tf$ - exclude: \.+.terraform\/.*$ + exclude: \.terraform\/.*$ require_serial: true - id: terrascan From 2e8dcf9298733a256cc7f8c6f05b3ef9a1047a36 Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Tue, 21 Dec 2021 21:50:12 +0200 Subject: [PATCH 194/214] fix: Speedup `terrascan` hook up to x3 times in big repos (#307) --- .pre-commit-hooks.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index f923dafb1..7b504706d 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -108,3 +108,4 @@ description: Runs terrascan on Terraform templates. language: script entry: terrascan.sh + require_serial: true From 096245800e76333b914489abb5157d71aafaebb7 Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Tue, 21 Dec 2021 21:51:14 +0200 Subject: [PATCH 195/214] chore: Release action should track hooks configuration changes (#308) --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7633f477f..9c51c327e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,6 +9,7 @@ on: - '**/*.py' - '**/*.sh' - 'Dockerfile' + - '.pre-commit-hooks.yaml' jobs: release: From 04ecd10343c066e91e3c4b6d53f111a54f0363fa Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Tue, 21 Dec 2021 19:52:23 +0000 Subject: [PATCH 196/214] chore(release): version 1.62.2 [skip ci] ## [1.62.2](https://github.com/antonbabenko/pre-commit-terraform/compare/v1.62.1...v1.62.2) (2021-12-21) ### Bug Fixes * Properly exclude .terraform directory with checkov hook ([#306](https://github.com/antonbabenko/pre-commit-terraform/issues/306)) ([b431a43](https://github.com/antonbabenko/pre-commit-terraform/commit/b431a43ffa6cd13156485ef853c967856e9572ef)) * Speedup `terrascan` hook up to x3 times in big repos ([#307](https://github.com/antonbabenko/pre-commit-terraform/issues/307)) ([2e8dcf9](https://github.com/antonbabenko/pre-commit-terraform/commit/2e8dcf9298733a256cc7f8c6f05b3ef9a1047a36)) --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fccfbc7fc..698ac00e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ All notable changes to this project will be documented in this file. +## [1.62.2](https://github.com/antonbabenko/pre-commit-terraform/compare/v1.62.1...v1.62.2) (2021-12-21) + + +### Bug Fixes + +* Properly exclude .terraform directory with checkov hook ([#306](https://github.com/antonbabenko/pre-commit-terraform/issues/306)) ([b431a43](https://github.com/antonbabenko/pre-commit-terraform/commit/b431a43ffa6cd13156485ef853c967856e9572ef)) +* Speedup `terrascan` hook up to x3 times in big repos ([#307](https://github.com/antonbabenko/pre-commit-terraform/issues/307)) ([2e8dcf9](https://github.com/antonbabenko/pre-commit-terraform/commit/2e8dcf9298733a256cc7f8c6f05b3ef9a1047a36)) + ## [1.62.1](https://github.com/antonbabenko/pre-commit-terraform/compare/v1.62.0...v1.62.1) (2021-12-18) From 66401d93f485164fb2272af297df835b932c61c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Miguel=20Bustillo=20Rodr=C3=ADguez?= <20931458+carlosbustillordguez@users.noreply.github.com> Date: Wed, 22 Dec 2021 19:44:53 +0100 Subject: [PATCH 197/214] fix: Check all directories with changes and pass all args in terrascan hook (#305) --- .pre-commit-hooks.yaml | 2 ++ README.md | 19 ++++++++++++++++++- terrascan.sh | 36 +++++++++++++++++++++++++++++------- 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 7b504706d..95a000ffb 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -108,4 +108,6 @@ description: Runs terrascan on Terraform templates. language: script entry: terrascan.sh + files: \.tf$ + exclude: \.terraform\/.*$ require_serial: true diff --git a/README.md b/README.md index 45f8b3049..2c2375a2b 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ If you are using `pre-commit-terraform` already or want to support its developme * [terraform_tflint](#terraform_tflint) * [terraform_tfsec](#terraform_tfsec) * [terraform_validate](#terraform_validate) + * [terrascan](#terrascan) * [Authors](#authors) * [License](#license) @@ -223,7 +224,7 @@ There are several [pre-commit](https://pre-commit.com/) hooks to keep Terraform | `terraform_validate` | Validates all Terraform configuration files. [Hook notes](#terraform_validate) | - | | `terragrunt_fmt` | Reformat all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) to a canonical format. | `terragrunt` | | `terragrunt_validate` | Validates all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) | `terragrunt` | -| `terrascan` | [terrascan](https://github.com/accurics/terrascan) Detect compliance and security violations. | `terrascan` | +| `terrascan` | [terrascan](https://github.com/accurics/terrascan) Detect compliance and security violations. [Hook notes](#terrascan) | `terrascan` | Check the [source file](https://github.com/antonbabenko/pre-commit-terraform/blob/master/.pre-commit-hooks.yaml) to know arguments used for each hook. @@ -550,6 +551,22 @@ Example: **Warning:** If you use Terraform workspaces, DO NOT use this workaround ([details](https://github.com/antonbabenko/pre-commit-terraform/issues/203#issuecomment-918791847)). Wait to [`force-init`](https://github.com/antonbabenko/pre-commit-terraform/issues/224) option implementation. +### terrascan + +1. `terrascan` supports custom arguments so you can pass supported flags like `--non-recursive` and `--policy-type` to disable recursive inspection and set the policy type respectively: + + ```yaml + - id: terrascan + args: + - --args=--non-recursive # avoids scan errors on subdirectories without Terraform config files + - --args=--policy-type=azure + ``` + + See the `terrascan run -h` command line help for available options. + +2. Use the `--args=--verbose` parameter to see the rule ID in the scaning output. Usuful to skip validations. +3. Use `--skip-rules="ruleID1,ruleID2"` parameter to skip one or more rules globally while scanning (e.g.: `--args=--skip-rules="ruleID1,ruleID2"`). +4. Use the syntax `#ts:skip=RuleID optional_comment` inside a resource to skip the rule for that resource. ## Authors diff --git a/terrascan.sh b/terrascan.sh index d8233068b..bd66a73de 100755 --- a/terrascan.sh +++ b/terrascan.sh @@ -4,27 +4,49 @@ set -eo pipefail main() { initialize_ parse_cmdline_ "$@" - - # propagate $FILES to custom function - terrascan_ "$ARGS" "$FILES" + terrascan_ "${ARGS[*]}" "${FILES[@]}" } terrascan_() { + local -r args="${1}" + shift 1 + local -a -r files=("$@") + # consume modified files passed from pre-commit so that # terrascan runs against only those relevant directories - for file_with_path in $FILES; do + for file_with_path in "${files[@]}"; do file_with_path="${file_with_path// /__REPLACED__SPACE__}" paths[index]=$(dirname "$file_with_path") - - let "index+=1" + index=$((index + 1)) done + # allow terrascan to continue if exit_code is greater than 0 + # preserve errexit status + shopt -qo errexit && ERREXIT_IS_SET=true + set +e + terrascan_final_exit_code=0 + + # for each path run terrascan for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do path_uniq="${path_uniq//__REPLACED__SPACE__/ }" pushd "$path_uniq" > /dev/null - terrascan scan -i terraform $ARGS + + # pass the arguments to terrascan + # shellcheck disable=SC2086 # terrascan fails when quoting is used ("$arg" vs $arg) + terrascan scan -i terraform $args + + local exit_code=$? + if [ $exit_code != 0 ]; then + terrascan_final_exit_code=$exit_code + fi + popd > /dev/null done + + # restore errexit if it was set before the "for" loop + [[ $ERREXIT_IS_SET ]] && set -e + # return the terrascan final exit_code + exit $terrascan_final_exit_code } initialize_() { From ac9299cf5747910d46edd12f311a3fd024f1cb90 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 22 Dec 2021 18:45:26 +0000 Subject: [PATCH 198/214] chore(release): version 1.62.3 [skip ci] ## [1.62.3](https://github.com/antonbabenko/pre-commit-terraform/compare/v1.62.2...v1.62.3) (2021-12-22) ### Bug Fixes * Check all directories with changes and pass all args in terrascan hook ([#305](https://github.com/antonbabenko/pre-commit-terraform/issues/305)) ([66401d9](https://github.com/antonbabenko/pre-commit-terraform/commit/66401d93f485164fb2272af297df835b932c61c3)) --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 698ac00e3..64a8c49e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. +## [1.62.3](https://github.com/antonbabenko/pre-commit-terraform/compare/v1.62.2...v1.62.3) (2021-12-22) + + +### Bug Fixes + +* Check all directories with changes and pass all args in terrascan hook ([#305](https://github.com/antonbabenko/pre-commit-terraform/issues/305)) ([66401d9](https://github.com/antonbabenko/pre-commit-terraform/commit/66401d93f485164fb2272af297df835b932c61c3)) + ## [1.62.2](https://github.com/antonbabenko/pre-commit-terraform/compare/v1.62.1...v1.62.2) (2021-12-21) From 1f16f09c6fb7c76af53a48dfce33626197615d51 Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Thu, 6 Jan 2022 13:21:52 +0200 Subject: [PATCH 199/214] chore: Refactor all hooks (#310) --- infracost_breakdown.sh | 20 +++-- terraform_docs.sh | 66 ++++++++--------- terraform_fmt.sh | 114 ++++++++++++++++++---------- terraform_providers_lock.sh | 143 ++++++++++++++++++++++++------------ terraform_tflint.sh | 137 +++++++++++++++++++++++----------- terraform_tfsec.sh | 127 ++++++++++++++++++++------------ terraform_validate.sh | 48 +++++------- terragrunt_fmt.sh | 112 ++++++++++++++++++++++++---- terragrunt_validate.sh | 112 ++++++++++++++++++++++++---- terrascan.sh | 136 ++++++++++++++++++---------------- 10 files changed, 675 insertions(+), 340 deletions(-) diff --git a/infracost_breakdown.sh b/infracost_breakdown.sh index ac0fdf911..c56e4cd73 100755 --- a/infracost_breakdown.sh +++ b/infracost_breakdown.sh @@ -8,15 +8,14 @@ function main { } function common::colorify { - # Colors. Provided as first string to first arg of function. # shellcheck disable=SC2034 - local -r red="$(tput setaf 1)" + local -r red="\e[0m\e[31m" # shellcheck disable=SC2034 - local -r green="$(tput setaf 2)" + local -r green="\e[0m\e[32m" # shellcheck disable=SC2034 - local -r yellow="$(tput setaf 3)" + local -r yellow="\e[0m\e[33m" # Color reset - local -r RESET="$(tput sgr0)" + local -r RESET="\e[0m" # Params start # local COLOR="${!1}" @@ -40,12 +39,11 @@ function common::initialize { . "$SCRIPT_DIR/lib_getopt" } -# common global arrays. -# Populated in `parse_cmdline` and can used in hooks functions -declare -a ARGS=() -declare -a HOOK_CONFIG=() -declare -a FILES=() function common::parse_cmdline { + # common global arrays. + # Populated via `common::parse_cmdline` and can be used inside hooks' functions + declare -g -a ARGS=() FILES=() HOOK_CONFIG=() + local argv argv=$(getopt -o a:,h: --long args:,hook-config: -- "$@") || return eval "set -- $argv" @@ -180,4 +178,4 @@ function infracost_breakdown_ { fi } -[[ ${BASH_SOURCE[0]} != "$0" ]] || main "$@" +[ "${BASH_SOURCE[0]}" != "$0" ] || main "$@" diff --git a/terraform_docs.sh b/terraform_docs.sh index 3b04afe24..0c1e71159 100755 --- a/terraform_docs.sh +++ b/terraform_docs.sh @@ -1,35 +1,31 @@ #!/usr/bin/env bash set -eo pipefail -main() { - initialize_ - parse_cmdline_ "$@" +function main { + common::initialize + common::parse_cmdline "$@" # Support for setting relative PATH to .terraform-docs.yml config. ARGS=${ARGS[*]/--config=/--config=$(pwd)\/} terraform_docs_ "${HOOK_CONFIG[*]}" "$ARGS" "${FILES[@]}" } -initialize_() { +function common::initialize { + local SCRIPT_DIR # 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")" + SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" # source getopt function # shellcheck source=lib_getopt - . "$_SCRIPT_DIR/lib_getopt" + . "$SCRIPT_DIR/lib_getopt" } -parse_cmdline_() { - declare argv - argv=$(getopt -o a: --long args:,hook-config: -- "$@") || return +function common::parse_cmdline { + # common global arrays. + # Populated via `common::parse_cmdline` and can be used inside hooks' functions + declare -g -a ARGS=() FILES=() HOOK_CONFIG=() + + local argv + argv=$(getopt -o a:,h: --long args:,hook-config: -- "$@") || return eval "set -- $argv" for argv; do @@ -39,9 +35,9 @@ parse_cmdline_() { ARGS+=("$1") shift ;; - --hook-config) + -h | --hook-config) shift - HOOK_CONFIG+=("$1") + HOOK_CONFIG+=("$1;") shift ;; --) @@ -53,12 +49,15 @@ parse_cmdline_() { done } -terraform_docs_() { +function terraform_docs_ { local -r hook_config="$1" local -r args="$2" shift 2 local -a -r files=("$@") + # Get hook settings + IFS=";" read -r -a configs <<< "$hook_config" + local hack_terraform_docs hack_terraform_docs=$(terraform version | sed -n 1p | grep -c 0.12) || true @@ -72,7 +71,7 @@ terraform_docs_() { if [[ -z "$is_old_terraform_docs" ]]; then # Using terraform-docs 0.8+ (preferred) - terraform_docs "0" "$hook_config" "$args" "${files[@]}" + 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 @@ -84,17 +83,17 @@ terraform_docs_() { local tmp_file_awk tmp_file_awk=$(mktemp "${TMPDIR:-/tmp}/terraform-docs-XXXXXXXXXX") terraform_docs_awk "$tmp_file_awk" - terraform_docs "$tmp_file_awk" "$hook_config" "$args" "${files[@]}" + 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" "$hook_config" "$args" "${files[@]}" + terraform_docs "0" "${configs[*]}" "$args" "${files[@]}" fi } -terraform_docs() { +function terraform_docs { local -r terraform_docs_awk_file="$1" local -r hook_config="$2" local -r args="$3" @@ -141,11 +140,11 @@ terraform_docs() { esac done - local path_uniq - for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do - path_uniq="${path_uniq//__REPLACED__SPACE__/ }" + local dir_path + for dir_path in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do + dir_path="${dir_path//__REPLACED__SPACE__/ }" - pushd "$path_uniq" > /dev/null + pushd "$dir_path" > /dev/null || continue # # Create file if it not exist and `--create-if-not-exist=true` provided @@ -212,7 +211,7 @@ terraform_docs() { done } -terraform_docs_awk() { +function terraform_docs_awk { local -r output_file=$1 cat << "EOF" > "$output_file" @@ -371,9 +370,4 @@ EOF } -# global arrays -declare -a ARGS=() -declare -a FILES=() -declare -a HOOK_CONFIG=() - -[[ ${BASH_SOURCE[0]} != "$0" ]] || main "$@" +[ "${BASH_SOURCE[0]}" != "$0" ] || main "$@" diff --git a/terraform_fmt.sh b/terraform_fmt.sh index 82e66f5e7..f6cad78c8 100755 --- a/terraform_fmt.sh +++ b/terraform_fmt.sh @@ -1,33 +1,29 @@ #!/usr/bin/env bash set -eo pipefail -main() { - initialize_ - parse_cmdline_ "$@" - terraform_fmt_ +function main { + common::initialize + common::parse_cmdline "$@" + terraform_fmt_ "${ARGS[*]}" "${FILES[@]}" } -initialize_() { +function common::initialize { + local SCRIPT_DIR # 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")" + SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" # source getopt function # shellcheck source=lib_getopt - . "$_SCRIPT_DIR/lib_getopt" + . "$SCRIPT_DIR/lib_getopt" } -parse_cmdline_() { - declare argv - argv=$(getopt -o a: --long args: -- "$@") || return +function common::parse_cmdline { + # common global arrays. + # Populated via `common::parse_cmdline` and can be used inside hooks' functions + declare -g -a ARGS=() FILES=() HOOK_CONFIG=() + + local argv + argv=$(getopt -o a:,h: --long args:,hook-config: -- "$@") || return eval "set -- $argv" for argv; do @@ -37,6 +33,11 @@ parse_cmdline_() { ARGS+=("$1") shift ;; + -h | --hook-config) + shift + HOOK_CONFIG+=("$1;") + shift + ;; --) shift FILES=("$@") @@ -46,44 +47,77 @@ parse_cmdline_() { done } -terraform_fmt_() { - - declare -a paths - declare -a tfvars_files - - index=0 - - for file_with_path in "${FILES[@]}"; do +function terraform_fmt_ { + local -r args="$1" + shift 1 + local -a -r files=("$@") + # consume modified files passed from pre-commit so that + # hook runs against only those relevant directories + local index=0 + for file_with_path in "${files[@]}"; do file_with_path="${file_with_path// /__REPLACED__SPACE__}" - paths[index]=$(dirname "$file_with_path") - + dir_paths[index]=$(dirname "$file_with_path") + # TODO Unique part if [[ "$file_with_path" == *".tfvars" ]]; then tfvars_files+=("$file_with_path") fi - + #? End for unique part ((index += 1)) done - for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do - path_uniq="${path_uniq//__REPLACED__SPACE__/ }" + # preserve errexit status + shopt -qo errexit && ERREXIT_IS_SET=true + # allow hook to continue if exit_code is greater than 0 + set +e + local final_exit_code=0 - ( - cd "$path_uniq" - terraform fmt "${ARGS[@]}" - ) + # run hook for each path + for dir_path in $(echo "${dir_paths[*]}" | tr ' ' '\n' | sort -u); do + dir_path="${dir_path//__REPLACED__SPACE__/ }" + pushd "$dir_path" > /dev/null || continue + + per_dir_hook_unique_part "$args" "$dir_path" + + local exit_code=$? + if [ $exit_code -ne 0 ]; then + final_exit_code=$exit_code + fi + + popd > /dev/null done + # TODO: Unique part # terraform.tfvars are excluded by `terraform fmt` for tfvars_file in "${tfvars_files[@]}"; do tfvars_file="${tfvars_file//__REPLACED__SPACE__/ }" terraform fmt "${ARGS[@]}" "$tfvars_file" + local exit_code=$? + if [ $exit_code -ne 0 ]; then + final_exit_code=$exit_code + fi done + #? End for unique part + # restore errexit if it was set before the "for" loop + [[ $ERREXIT_IS_SET ]] && set -e + # return the hook final exit_code + exit $final_exit_code + } -# global arrays -declare -a ARGS=() -declare -a FILES=() +function per_dir_hook_unique_part { + # common logic located in common::per_dir_hook + local -r args="$1" + local -r dir_path="$2" + + # pass the arguments to hook + # shellcheck disable=SC2068 # hook fails when quoting is used ("$arg[@]") + terraform fmt ${args[@]} + + # return exit code to common::per_dir_hook + local exit_code=$? + return $exit_code +} -[[ ${BASH_SOURCE[0]} != "$0" ]] || main "$@" +[ "${BASH_SOURCE[0]}" != "$0" ] || main "$@" diff --git a/terraform_providers_lock.sh b/terraform_providers_lock.sh index 31ea63f78..26bf40b58 100755 --- a/terraform_providers_lock.sh +++ b/terraform_providers_lock.sh @@ -2,33 +2,51 @@ set -eo pipefail -main() { - initialize_ - parse_cmdline_ "$@" - terraform_providers_lock_ +function main { + common::initialize + common::parse_cmdline "$@" + common::per_dir_hook "${ARGS[*]}" "${FILES[@]}" } -initialize_() { +function common::colorify { + # shellcheck disable=SC2034 + local -r red="\e[0m\e[31m" + # shellcheck disable=SC2034 + local -r green="\e[0m\e[32m" + # shellcheck disable=SC2034 + local -r yellow="\e[0m\e[33m" + # Color reset + local -r RESET="\e[0m" + + # Params start # + local COLOR="${!1}" + local -r TEXT=$2 + # Params end # + + if [ "$PRE_COMMIT_COLOR" = "never" ]; then + COLOR=$RESET + fi + + echo -e "${COLOR}${TEXT}${RESET}" +} + +function common::initialize { + local SCRIPT_DIR # 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")" + SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" # source getopt function # shellcheck source=lib_getopt - . "$_SCRIPT_DIR/lib_getopt" + . "$SCRIPT_DIR/lib_getopt" } -parse_cmdline_() { - declare argv - argv=$(getopt -o a: --long args: -- "$@") || return +function common::parse_cmdline { + # common global arrays. + # Populated via `common::parse_cmdline` and can be used inside hooks' functions + declare -g -a ARGS=() FILES=() HOOK_CONFIG=() + + local argv + argv=$(getopt -o a:,h: --long args:,hook-config: -- "$@") || return eval "set -- $argv" for argv; do @@ -38,6 +56,11 @@ parse_cmdline_() { ARGS+=("$1") shift ;; + -h | --hook-config) + shift + HOOK_CONFIG+=("$1;") + shift + ;; --) shift FILES=("$@") @@ -47,42 +70,72 @@ parse_cmdline_() { done } -terraform_providers_lock_() { - local -a paths - local index=0 - local file_with_path +function common::per_dir_hook { + local -r args="$1" + shift 1 + local -a -r files=("$@") - for file_with_path in "${FILES[@]}"; do + # consume modified files passed from pre-commit so that + # hook runs against only those relevant directories + local index=0 + for file_with_path in "${files[@]}"; do file_with_path="${file_with_path// /__REPLACED__SPACE__}" - paths[index]=$(dirname "$file_with_path") + dir_paths[index]=$(dirname "$file_with_path") ((index += 1)) done - local path_uniq - for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do - path_uniq="${path_uniq//__REPLACED__SPACE__/ }" - - if [[ ! -d "${path_uniq}/.terraform" ]]; then - set +e - init_output=$(terraform -chdir="${path_uniq}" init -backend=false 2>&1) - init_code=$? - set -e - - if [[ $init_code != 0 ]]; then - echo "Init before validation failed: $path_uniq" - echo "$init_output" - exit 1 - fi + # preserve errexit status + shopt -qo errexit && ERREXIT_IS_SET=true + # allow hook to continue if exit_code is greater than 0 + set +e + local final_exit_code=0 + + # run hook for each path + for dir_path in $(echo "${dir_paths[*]}" | tr ' ' '\n' | sort -u); do + dir_path="${dir_path//__REPLACED__SPACE__/ }" + pushd "$dir_path" > /dev/null || continue + + per_dir_hook_unique_part "$args" "$dir_path" + + local exit_code=$? + if [ $exit_code -ne 0 ]; then + final_exit_code=$exit_code fi - terraform -chdir="${path_uniq}" providers lock "${ARGS[@]}" + popd > /dev/null done + + # restore errexit if it was set before the "for" loop + [[ $ERREXIT_IS_SET ]] && set -e + # return the hook final exit_code + exit $final_exit_code } -# global arrays -declare -a ARGS -declare -a FILES +function per_dir_hook_unique_part { + # common logic located in common::per_dir_hook + local -r args="$1" + local -r dir_path="$2" + + if [ ! -d ".terraform" ]; then + init_output=$(terraform init -backend=false 2>&1) + init_code=$? + + if [ $init_code -ne 0 ]; then + common::colorify "red" "Init before validation failed: $dir_path" + common::colorify "red" "$init_output" + exit $init_code + fi + fi + + # pass the arguments to hook + # shellcheck disable=SC2068 # hook fails when quoting is used ("$arg[@]") + terraform providers lock ${args[@]} + + # return exit code to common::per_dir_hook + local exit_code=$? + return $exit_code +} -[[ ${BASH_SOURCE[0]} != "$0" ]] || main "$@" +[ "${BASH_SOURCE[0]}" != "$0" ] || main "$@" diff --git a/terraform_tflint.sh b/terraform_tflint.sh index 87c120383..d6501b84f 100755 --- a/terraform_tflint.sh +++ b/terraform_tflint.sh @@ -2,41 +2,65 @@ set -eo pipefail -main() { - initialize_ - parse_cmdline_ "$@" - tflint_ +function main { + common::initialize + common::parse_cmdline "$@" + # Support for setting PATH to repo root. + ARGS=${ARGS[*]/__GIT_WORKING_DIR__/$(pwd)\/} + common::per_dir_hook "$ARGS" "${FILES[@]}" } -initialize_() { +function common::colorify { + # shellcheck disable=SC2034 + local -r red="\e[0m\e[31m" + # shellcheck disable=SC2034 + local -r green="\e[0m\e[32m" + # shellcheck disable=SC2034 + local -r yellow="\e[0m\e[33m" + # Color reset + local -r RESET="\e[0m" + + # Params start # + local COLOR="${!1}" + local -r TEXT=$2 + # Params end # + + if [ "$PRE_COMMIT_COLOR" = "never" ]; then + COLOR=$RESET + fi + + echo -e "${COLOR}${TEXT}${RESET}" +} + +function common::initialize { + local SCRIPT_DIR # 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")" + SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" # source getopt function # shellcheck source=lib_getopt - . "$_SCRIPT_DIR/lib_getopt" + . "$SCRIPT_DIR/lib_getopt" } -parse_cmdline_() { - declare argv - argv=$(getopt -o a: --long args: -- "$@") || return +function common::parse_cmdline { + # common global arrays. + # Populated via `common::parse_cmdline` and can be used inside hooks' functions + declare -g -a ARGS=() FILES=() HOOK_CONFIG=() + + local argv + argv=$(getopt -o a:,h: --long args:,hook-config: -- "$@") || return eval "set -- $argv" for argv; do case $argv in -a | --args) shift - expanded_arg="${1//__GIT_WORKING_DIR__/$PWD}" - ARGS+=("$expanded_arg") + ARGS+=("$1") + shift + ;; + -h | --hook-config) + shift + HOOK_CONFIG+=("$1;") shift ;; --) @@ -46,43 +70,68 @@ parse_cmdline_() { ;; esac done - } -tflint_() { +function common::per_dir_hook { + local -r args="$1" + shift 1 + local -a -r files=("$@") + + # consume modified files passed from pre-commit so that + # hook runs against only those relevant directories local index=0 - for file_with_path in "${FILES[@]}"; do + for file_with_path in "${files[@]}"; do file_with_path="${file_with_path// /__REPLACED__SPACE__}" - paths[index]=$(dirname "$file_with_path") + dir_paths[index]=$(dirname "$file_with_path") ((index += 1)) done + + # preserve errexit status + shopt -qo errexit && ERREXIT_IS_SET=true + # allow hook to continue if exit_code is greater than 0 set +e - tflint_final_exit_code=0 - for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do - path_uniq="${path_uniq//__REPLACED__SPACE__/ }" - pushd "$path_uniq" > /dev/null - - # Print checked PATH **only** if TFLint have any messages - # shellcheck disable=SC2091 # Suppress error output - $(tflint "${ARGS[@]}" 2>&1) 2> /dev/null || { - echo >&2 -e "\033[1;33m\nTFLint in $path_uniq/:\033[0m" - tflint "${ARGS[@]}" - } + local final_exit_code=0 + + # run hook for each path + for dir_path in $(echo "${dir_paths[*]}" | tr ' ' '\n' | sort -u); do + dir_path="${dir_path//__REPLACED__SPACE__/ }" + pushd "$dir_path" > /dev/null || continue + + per_dir_hook_unique_part "$args" "$dir_path" + local exit_code=$? - if [ $exit_code != 0 ]; then - tflint_final_exit_code=$exit_code + if [ $exit_code -ne 0 ]; then + final_exit_code=$exit_code fi popd > /dev/null done - set -e - exit $tflint_final_exit_code + + # restore errexit if it was set before the "for" loop + [[ $ERREXIT_IS_SET ]] && set -e + # return the hook final exit_code + exit $final_exit_code } -# global arrays -declare -a ARGS -declare -a FILES +function per_dir_hook_unique_part { + # common logic located in common::per_dir_hook + local -r args="$1" + local -r dir_path="$2" + + # Print checked PATH **only** if TFLint have any messages + # shellcheck disable=SC2091,SC2068 # Suppress error output + $(tflint ${args[@]} 2>&1) 2> /dev/null || { + common::colorify "yellow" "TFLint in $dir_path/:" + + # shellcheck disable=SC2068 # hook fails when quoting is used ("$arg[@]") + tflint ${args[@]} + } + + # return exit code to common::per_dir_hook + local exit_code=$? + return $exit_code +} -[[ ${BASH_SOURCE[0]} != "$0" ]] || main "$@" +[ "${BASH_SOURCE[0]}" != "$0" ] || main "$@" diff --git a/terraform_tfsec.sh b/terraform_tfsec.sh index 0dc01fe10..a68905bca 100755 --- a/terraform_tfsec.sh +++ b/terraform_tfsec.sh @@ -1,74 +1,109 @@ #!/usr/bin/env bash set -eo pipefail -main() { - initialize_ - parse_cmdline_ "$@" - - # propagate $FILES to custom function - tfsec_ "$ARGS" "${FILES[*]}" +function main { + common::initialize + common::parse_cmdline "$@" + # Support for setting PATH to repo root. + ARGS=${ARGS[*]/__GIT_WORKING_DIR__/$(pwd)\/} + common::per_dir_hook "$ARGS" "${FILES[@]}" } -tfsec_() { - # consume modified files passed from pre-commit so that - # tfsec runs against only those relevant directories - for file_with_path in ${FILES[*]}; do - file_with_path="${file_with_path// /__REPLACED__SPACE__}" - paths[index]=$(dirname "$file_with_path") - - let "index+=1" - done - - for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do - path_uniq="${path_uniq//__REPLACED__SPACE__/ }" - pushd "$path_uniq" > /dev/null - tfsec $ARGS - popd > /dev/null - done -} - -initialize_() { +function common::initialize { + local SCRIPT_DIR # 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")" + SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" # source getopt function # shellcheck source=lib_getopt - . "$_SCRIPT_DIR/lib_getopt" + . "$SCRIPT_DIR/lib_getopt" } -parse_cmdline_() { - declare argv - argv=$(getopt -o a: --long args: -- "$@") || return +function common::parse_cmdline { + # common global arrays. + # Populated via `common::parse_cmdline` and can be used inside hooks' functions + declare -g -a ARGS=() FILES=() HOOK_CONFIG=() + + local argv + argv=$(getopt -o a:,h: --long args:,hook-config: -- "$@") || return eval "set -- $argv" for argv; do case $argv in -a | --args) shift - expanded_arg="${1//__GIT_WORKING_DIR__/$PWD}" - ARGS+=("$expanded_arg") + ARGS+=("$1") + shift + ;; + -h | --hook-config) + shift + HOOK_CONFIG+=("$1;") shift ;; --) shift - FILES+=("$@") + FILES=("$@") break ;; esac done } -# global arrays -declare -a ARGS=() -declare -a FILES=() +function common::per_dir_hook { + local -r args="$1" + shift 1 + local -a -r files=("$@") + + # consume modified files passed from pre-commit so that + # hook runs against only those relevant directories + local index=0 + for file_with_path in "${files[@]}"; do + file_with_path="${file_with_path// /__REPLACED__SPACE__}" + + dir_paths[index]=$(dirname "$file_with_path") + + ((index += 1)) + done + + # preserve errexit status + shopt -qo errexit && ERREXIT_IS_SET=true + # allow hook to continue if exit_code is greater than 0 + set +e + local final_exit_code=0 + + # run hook for each path + for dir_path in $(echo "${dir_paths[*]}" | tr ' ' '\n' | sort -u); do + dir_path="${dir_path//__REPLACED__SPACE__/ }" + pushd "$dir_path" > /dev/null || continue + + per_dir_hook_unique_part "$args" "$dir_path" + + local exit_code=$? + if [ $exit_code -ne 0 ]; then + final_exit_code=$exit_code + fi + + popd > /dev/null + done + + # restore errexit if it was set before the "for" loop + [[ $ERREXIT_IS_SET ]] && set -e + # return the hook final exit_code + exit $final_exit_code +} + +function per_dir_hook_unique_part { + # common logic located in common::per_dir_hook + local -r args="$1" + local -r dir_path="$2" + + # pass the arguments to hook + # shellcheck disable=SC2068 # hook fails when quoting is used ("$arg[@]") + tfsec ${args[@]} + + # return exit code to common::per_dir_hook + local exit_code=$? + return $exit_code +} -[[ ${BASH_SOURCE[0]} != "$0" ]] || main "$@" +[ "${BASH_SOURCE[0]}" != "$0" ] || main "$@" diff --git a/terraform_validate.sh b/terraform_validate.sh index ea23dac7f..655bc09ac 100755 --- a/terraform_validate.sh +++ b/terraform_validate.sh @@ -4,31 +4,23 @@ set -eo pipefail # `terraform validate` requires this env variable to be set export AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION:-us-east-1} -main() { - initialize_ +function main { + common::initialize parse_cmdline_ "$@" terraform_validate_ } -initialize_() { +function common::initialize { + local SCRIPT_DIR # 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")" + SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" # source getopt function # shellcheck source=lib_getopt - . "$_SCRIPT_DIR/lib_getopt" + . "$SCRIPT_DIR/lib_getopt" } -parse_cmdline_() { +function parse_cmdline_ { declare argv argv=$(getopt -o e:i:a: --long envs:,init-args:,args: -- "$@") || return eval "set -- $argv" @@ -59,7 +51,7 @@ parse_cmdline_() { done } -terraform_validate_() { +function terraform_validate_ { # Setup environment variables local var var_name var_value @@ -82,23 +74,23 @@ terraform_validate_() { ((index += 1)) done - local path_uniq - for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do - path_uniq="${path_uniq//__REPLACED__SPACE__/ }" + local dir_path + for dir_path in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do + dir_path="${dir_path//__REPLACED__SPACE__/ }" - if [[ -n "$(find "$path_uniq" -maxdepth 1 -name '*.tf' -print -quit)" ]]; then + if [[ -n "$(find "$dir_path" -maxdepth 1 -name '*.tf' -print -quit)" ]]; then - pushd "$(realpath "$path_uniq")" > /dev/null + pushd "$(realpath "$dir_path")" > /dev/null - if [[ ! -d .terraform ]]; then + if [ ! -d .terraform ]; then set +e init_output=$(terraform init -backend=false "${INIT_ARGS[@]}" 2>&1) init_code=$? set -e - if [[ $init_code != 0 ]]; then + if [ $init_code -ne 0 ]; then error=1 - echo "Init before validation failed: $path_uniq" + echo "Init before validation failed: $dir_path" echo "$init_output" popd > /dev/null continue @@ -110,9 +102,9 @@ terraform_validate_() { validate_code=$? set -e - if [[ $validate_code != 0 ]]; then + if [ $validate_code -ne 0 ]; then error=1 - echo "Validation failed: $path_uniq" + echo "Validation failed: $dir_path" echo "$validate_output" echo fi @@ -121,7 +113,7 @@ terraform_validate_() { fi done - if [[ $error -ne 0 ]]; then + if [ $error -ne 0 ]; then exit 1 fi } @@ -132,4 +124,4 @@ declare -a INIT_ARGS declare -a ENVS declare -a FILES -[[ ${BASH_SOURCE[0]} != "$0" ]] || main "$@" +[ "${BASH_SOURCE[0]}" != "$0" ] || main "$@" diff --git a/terragrunt_fmt.sh b/terragrunt_fmt.sh index ee23131e7..42f9b5ed5 100755 --- a/terragrunt_fmt.sh +++ b/terragrunt_fmt.sh @@ -1,23 +1,107 @@ #!/usr/bin/env bash +set -eo pipefail -set -e +function main { + common::initialize + common::parse_cmdline "$@" + common::per_dir_hook "${ARGS[*]}" "${FILES[@]}" +} -declare -a paths +function common::initialize { + local SCRIPT_DIR + # get directory containing this script + SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" -index=0 + # source getopt function + # shellcheck source=lib_getopt + . "$SCRIPT_DIR/lib_getopt" +} -for file_with_path in "$@"; do - file_with_path="${file_with_path// /__REPLACED__SPACE__}" +function common::parse_cmdline { + # common global arrays. + # Populated via `common::parse_cmdline` and can be used inside hooks' functions + declare -g -a ARGS=() FILES=() HOOK_CONFIG=() - paths[index]=$(dirname "$file_with_path") + local argv + argv=$(getopt -o a:,h: --long args:,hook-config: -- "$@") || return + eval "set -- $argv" - let "index+=1" -done + for argv; do + case $argv in + -a | --args) + shift + ARGS+=("$1") + shift + ;; + -h | --hook-config) + shift + HOOK_CONFIG+=("$1;") + shift + ;; + --) + shift + FILES=("$@") + break + ;; + esac + done +} -for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do - path_uniq="${path_uniq//__REPLACED__SPACE__/ }" +function common::per_dir_hook { + local -r args="$1" + shift 1 + local -a -r files=("$@") - pushd "$path_uniq" > /dev/null - terragrunt hclfmt - popd > /dev/null -done + # consume modified files passed from pre-commit so that + # hook runs against only those relevant directories + local index=0 + for file_with_path in "${files[@]}"; do + file_with_path="${file_with_path// /__REPLACED__SPACE__}" + + dir_paths[index]=$(dirname "$file_with_path") + + ((index += 1)) + done + + # preserve errexit status + shopt -qo errexit && ERREXIT_IS_SET=true + # allow hook to continue if exit_code is greater than 0 + set +e + local final_exit_code=0 + + # run hook for each path + for dir_path in $(echo "${dir_paths[*]}" | tr ' ' '\n' | sort -u); do + dir_path="${dir_path//__REPLACED__SPACE__/ }" + pushd "$dir_path" > /dev/null || continue + + per_dir_hook_unique_part "$args" "$dir_path" + + local exit_code=$? + if [ $exit_code -ne 0 ]; then + final_exit_code=$exit_code + fi + + popd > /dev/null + done + + # restore errexit if it was set before the "for" loop + [[ $ERREXIT_IS_SET ]] && set -e + # return the hook final exit_code + exit $final_exit_code +} + +function per_dir_hook_unique_part { + # common logic located in common::per_dir_hook + local -r args="$1" + local -r dir_path="$2" + + # pass the arguments to hook + # shellcheck disable=SC2068 # hook fails when quoting is used ("$arg[@]") + terragrunt hclfmt ${args[@]} + + # return exit code to common::per_dir_hook + local exit_code=$? + return $exit_code +} + +[ "${BASH_SOURCE[0]}" != "$0" ] || main "$@" diff --git a/terragrunt_validate.sh b/terragrunt_validate.sh index 7f0cf5849..1fd83a1dc 100755 --- a/terragrunt_validate.sh +++ b/terragrunt_validate.sh @@ -1,23 +1,107 @@ #!/usr/bin/env bash +set -eo pipefail -set -e +function main { + common::initialize + common::parse_cmdline "$@" + common::per_dir_hook "${ARGS[*]}" "${FILES[@]}" +} -declare -a paths +function common::initialize { + local SCRIPT_DIR + # get directory containing this script + SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" -index=0 + # source getopt function + # shellcheck source=lib_getopt + . "$SCRIPT_DIR/lib_getopt" +} -for file_with_path in "$@"; do - file_with_path="${file_with_path// /__REPLACED__SPACE__}" +function common::parse_cmdline { + # common global arrays. + # Populated via `common::parse_cmdline` and can be used inside hooks' functions + declare -g -a ARGS=() FILES=() HOOK_CONFIG=() - paths[index]=$(dirname "$file_with_path") + local argv + argv=$(getopt -o a:,h: --long args:,hook-config: -- "$@") || return + eval "set -- $argv" - let "index+=1" -done + for argv; do + case $argv in + -a | --args) + shift + ARGS+=("$1") + shift + ;; + -h | --hook-config) + shift + HOOK_CONFIG+=("$1;") + shift + ;; + --) + shift + FILES=("$@") + break + ;; + esac + done +} -for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do - path_uniq="${path_uniq//__REPLACED__SPACE__/ }" +function common::per_dir_hook { + local -r args="$1" + shift 1 + local -a -r files=("$@") - pushd "$path_uniq" > /dev/null - terragrunt validate - popd > /dev/null -done + # consume modified files passed from pre-commit so that + # hook runs against only those relevant directories + local index=0 + for file_with_path in "${files[@]}"; do + file_with_path="${file_with_path// /__REPLACED__SPACE__}" + + dir_paths[index]=$(dirname "$file_with_path") + + ((index += 1)) + done + + # preserve errexit status + shopt -qo errexit && ERREXIT_IS_SET=true + # allow hook to continue if exit_code is greater than 0 + set +e + local final_exit_code=0 + + # run hook for each path + for dir_path in $(echo "${dir_paths[*]}" | tr ' ' '\n' | sort -u); do + dir_path="${dir_path//__REPLACED__SPACE__/ }" + pushd "$dir_path" > /dev/null || continue + + per_dir_hook_unique_part "$args" "$dir_path" + + local exit_code=$? + if [ $exit_code -ne 0 ]; then + final_exit_code=$exit_code + fi + + popd > /dev/null + done + + # restore errexit if it was set before the "for" loop + [[ $ERREXIT_IS_SET ]] && set -e + # return the hook final exit_code + exit $final_exit_code +} + +function per_dir_hook_unique_part { + # common logic located in common::per_dir_hook + local -r args="$1" + local -r dir_path="$2" + + # pass the arguments to hook + # shellcheck disable=SC2068 # hook fails when quoting is used ("$arg[@]") + terragrunt validate ${args[@]} + + # return exit code to common::per_dir_hook + local exit_code=$? + return $exit_code +} + +[ "${BASH_SOURCE[0]}" != "$0" ] || main "$@" diff --git a/terrascan.sh b/terrascan.sh index bd66a73de..1ed33bc4a 100755 --- a/terrascan.sh +++ b/terrascan.sh @@ -1,43 +1,84 @@ #!/usr/bin/env bash set -eo pipefail -main() { - initialize_ - parse_cmdline_ "$@" - terrascan_ "${ARGS[*]}" "${FILES[@]}" +function main { + common::initialize + common::parse_cmdline "$@" + common::per_dir_hook "${ARGS[*]}" "${FILES[@]}" } -terrascan_() { - local -r args="${1}" +function common::initialize { + local SCRIPT_DIR + # get directory containing this script + SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" + + # source getopt function + # shellcheck source=lib_getopt + . "$SCRIPT_DIR/lib_getopt" +} + +function common::parse_cmdline { + # common global arrays. + # Populated via `common::parse_cmdline` and can be used inside hooks' functions + declare -g -a ARGS=() FILES=() HOOK_CONFIG=() + + local argv + argv=$(getopt -o a:,h: --long args:,hook-config: -- "$@") || return + eval "set -- $argv" + + for argv; do + case $argv in + -a | --args) + shift + ARGS+=("$1") + shift + ;; + -h | --hook-config) + shift + HOOK_CONFIG+=("$1;") + shift + ;; + --) + shift + FILES=("$@") + break + ;; + esac + done +} + +function common::per_dir_hook { + local -r args="$1" shift 1 local -a -r files=("$@") # consume modified files passed from pre-commit so that - # terrascan runs against only those relevant directories + # hook runs against only those relevant directories + local index=0 for file_with_path in "${files[@]}"; do file_with_path="${file_with_path// /__REPLACED__SPACE__}" - paths[index]=$(dirname "$file_with_path") - index=$((index + 1)) + + dir_paths[index]=$(dirname "$file_with_path") + + ((index += 1)) done - # allow terrascan to continue if exit_code is greater than 0 # preserve errexit status shopt -qo errexit && ERREXIT_IS_SET=true + # allow hook to continue if exit_code is greater than 0 set +e - terrascan_final_exit_code=0 + local final_exit_code=0 - # for each path run terrascan - for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do - path_uniq="${path_uniq//__REPLACED__SPACE__/ }" - pushd "$path_uniq" > /dev/null + # run hook for each path + for dir_path in $(echo "${dir_paths[*]}" | tr ' ' '\n' | sort -u); do + dir_path="${dir_path//__REPLACED__SPACE__/ }" + pushd "$dir_path" > /dev/null || continue - # pass the arguments to terrascan - # shellcheck disable=SC2086 # terrascan fails when quoting is used ("$arg" vs $arg) - terrascan scan -i terraform $args + per_dir_hook_unique_part "$args" "$dir_path" local exit_code=$? - if [ $exit_code != 0 ]; then - terrascan_final_exit_code=$exit_code + if [ $exit_code -ne 0 ]; then + final_exit_code=$exit_code fi popd > /dev/null @@ -45,51 +86,22 @@ terrascan_() { # restore errexit if it was set before the "for" loop [[ $ERREXIT_IS_SET ]] && set -e - # return the terrascan final exit_code - exit $terrascan_final_exit_code + # return the hook final exit_code + exit $final_exit_code } -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")" +function per_dir_hook_unique_part { + # common logic located in common::per_dir_hook + local -r args="$1" + local -r dir_path="$2" - # source getopt function - # shellcheck source=lib_getopt - . "$_SCRIPT_DIR/lib_getopt" -} - -parse_cmdline_() { - declare argv - argv=$(getopt -o a: --long args: -- "$@") || return - eval "set -- $argv" + # pass the arguments to hook + # shellcheck disable=SC2068 # hook fails when quoting is used ("$arg[@]") + terrascan scan -i terraform ${args[@]} - for argv; do - case $argv in - -a | --args) - shift - ARGS+=("$1") - shift - ;; - --) - shift - FILES+=("$@") - break - ;; - esac - done + # return exit code to common::per_dir_hook + local exit_code=$? + return $exit_code } -# global arrays -declare -a ARGS=() -declare -a FILES=() - -[[ ${BASH_SOURCE[0]} != "$0" ]] || main "$@" +[ "${BASH_SOURCE[0]}" != "$0" ] || main "$@" From 645f3fd126ba875ed4401ff3dc04dec8e391cbe0 Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Thu, 6 Jan 2022 13:25:32 +0200 Subject: [PATCH 200/214] chore: Cleanup file with test data (#311) --- .github/CONTRIBUTING.md | 4 ++++ .github/workflows/release.yml | 3 ++- tests/hooks_performance_test.sh | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index dc3191a40..7c4178c42 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -56,6 +56,10 @@ Script accept next options: | 5 | `RAW_TEST_`
`RESULTS_FILE_NAME` | `terraform_tfsec_pr123` | (Temporary) File where all test data will be stored. | +> **Note:** To make test results repeatable and comparable, be sure that on the test machine nothing generates an unstable workload. During tests good to stop any other apps and do not interact with the test machine. +> +> Otherwise, for eg, when you watch Youtube videos during one test and not during other, test results can differ up to 30% for the same test. + ### Run via BASH ```bash diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9c51c327e..3018771ab 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,7 +10,8 @@ on: - '**/*.sh' - 'Dockerfile' - '.pre-commit-hooks.yaml' - + # Ignore paths + - '!tests/**' jobs: release: name: Release diff --git a/tests/hooks_performance_test.sh b/tests/hooks_performance_test.sh index 77a353f4a..342bb302e 100755 --- a/tests/hooks_performance_test.sh +++ b/tests/hooks_performance_test.sh @@ -17,6 +17,8 @@ function run_tests { RESULTS_DIR="$(pwd)/tests/results" cd "$TEST_DIR" || { echo "Specified TEST_DIR does not exist" && exit 1; } + # Cleanup + rm "$RESULTS_DIR/$FILE_NAME_TO_SAVE_TEST_RESULTS" for ((i = 1; i <= TEST_NUM; i++)); do { From 3045dd55a3c0c5557f707ad3b0e841218008d19f Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Thu, 6 Jan 2022 15:08:18 +0200 Subject: [PATCH 201/214] chore: Add shellcheck and make checks passing (#315) Co-authored-by: Anton Babenko --- .github/workflows/pre-commit.yaml | 4 ++++ .pre-commit-config.yaml | 1 + infracost_breakdown.sh | 4 +++- terraform_docs.sh | 14 ++++++++----- tests/hooks_performance_test.sh | 35 +++++++++++++++---------------- 5 files changed, 34 insertions(+), 24 deletions(-) diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index 07241d1a0..6f2ec84ff 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -21,6 +21,10 @@ jobs: run: | curl -L "$(curl -s https://api.github.com/repos/mvdan/sh/releases/latest | grep -o -E -m 1 "https://.+?linux_amd64")" > shfmt \ && chmod +x shfmt && sudo mv shfmt /usr/bin/ + + - name: Install shellcheck + run: | + sudo apt update && sudo apt install shellcheck # Need to success pre-commit fix push - uses: actions/checkout@v2 with: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f1a7a65ef..f876ccd35 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -33,3 +33,4 @@ repos: hooks: - id: shfmt args: ['-l', '-i', '2', '-ci', '-sr', '-w'] + - id: shellcheck diff --git a/infracost_breakdown.sh b/infracost_breakdown.sh index c56e4cd73..f2cb816dd 100755 --- a/infracost_breakdown.sh +++ b/infracost_breakdown.sh @@ -62,6 +62,7 @@ function common::parse_cmdline { ;; --) shift + # shellcheck disable=SC2034 # Common function FILES=("$@") break ;; @@ -101,6 +102,7 @@ function infracost_breakdown_ { # $hook_config receives string like '1 > 2; 3 == 4;' etc. # It gets split by `;` into array, which we're parsing here ('1 > 2' ' 3 == 4') # Next line removes leading spaces, just for fancy output reason. + # shellcheck disable=SC2001 # Rule exception check=$(echo "$check" | sed 's/^[[:space:]]*//') # Drop quotes in hook args section. From: @@ -116,7 +118,7 @@ function infracost_breakdown_ { }; then check="${check:1:-1}" fi - + # shellcheck disable=SC2207 # Can't find working `read` command operations=($(echo "$check" | grep -oE '[!<>=]{1,2}')) # Get the very last operator, that is used in comparison inside `jq` query. # From the example below we need to pick the `>` which is in between `add` and `1000`, diff --git a/terraform_docs.sh b/terraform_docs.sh index 0c1e71159..f2efb0af7 100755 --- a/terraform_docs.sh +++ b/terraform_docs.sh @@ -121,9 +121,11 @@ function terraform_docs { local add_to_existing=false local create_if_not_exist=false - configs=($hook_config) + read -r -a configs <<< "$hook_config" + for c in "${configs[@]}"; do - config=(${c//=/ }) + + IFS="=" read -r -a config <<< "$c" key=${config[0]} value=${config[1]} @@ -161,9 +163,11 @@ function terraform_docs { dir="$(dirname "$text_file")" mkdir -p "$dir" - echo -e "# ${PWD##*/}\n" >> "$text_file" - echo "" >> "$text_file" - echo "" >> "$text_file" + { + echo -e "# ${PWD##*/}\n" + echo "" + echo "" + } >> "$text_file" fi # If file still not exist - skip dir diff --git a/tests/hooks_performance_test.sh b/tests/hooks_performance_test.sh index 342bb302e..4f35fce23 100755 --- a/tests/hooks_performance_test.sh +++ b/tests/hooks_performance_test.sh @@ -44,33 +44,33 @@ function generate_table { | time command | max | min | mean | median | | -------------- | ------ | ------ | -------- | ------ | | users seconds | $( - printf %"s\n" $users_seconds | datamash max 1 + printf %"s\n" "$users_seconds" | datamash max 1 ) | $( - printf %"s\n" $users_seconds | datamash min 1 + printf %"s\n" "$users_seconds" | datamash min 1 ) | $( - printf %"s\n" $users_seconds | datamash mean 1 - ) | $(printf %"s\n" $users_seconds | datamash median 1) | + printf %"s\n" "$users_seconds" | datamash mean 1 + ) | $(printf %"s\n" "$users_seconds" | datamash median 1) | | system seconds | $( - printf %"s\n" $system_seconds | datamash max 1 + printf %"s\n" "$system_seconds" | datamash max 1 ) | $( - printf %"s\n" $system_seconds | datamash min 1 + printf %"s\n" "$system_seconds" | datamash min 1 ) | $( - printf %"s\n" $system_seconds | datamash mean 1 - ) | $(printf %"s\n" $system_seconds | datamash median 1) | + printf %"s\n" "$system_seconds" | datamash mean 1 + ) | $(printf %"s\n" "$system_seconds" | datamash median 1) | | CPU % | $( - printf %"s\n" $cpu | datamash max 1 + printf %"s\n" "$cpu" | datamash max 1 ) | $( - printf %"s\n" $cpu | datamash min 1 + printf %"s\n" "$cpu" | datamash min 1 ) | $( - printf %"s\n" $cpu | datamash mean 1 - ) | $(printf %"s\n" $cpu | datamash median 1) | + printf %"s\n" "$cpu" | datamash mean 1 + ) | $(printf %"s\n" "$cpu" | datamash median 1) | | Total time | $( - printf %"s\n" $total_time | datamash max 1 + printf %"s\n" "$total_time" | datamash max 1 ) | $( - printf %"s\n" $total_time | datamash min 1 + printf %"s\n" "$total_time" | datamash min 1 ) | $( - printf %"s\n" $total_time | datamash mean 1 - ) | $(printf %"s\n" $total_time | datamash median 1) | + printf %"s\n" "$total_time" | datamash mean 1 + ) | $(printf %"s\n" "$total_time" | datamash median 1) | " } @@ -82,8 +82,7 @@ function save_result { local FILE_NAME=${5:-"tests_result.md"} - echo -e "\n$DESCRIPTION" >> "tests/results/$FILE_NAME" - echo -e "$TABLE" >> "tests/results/$FILE_NAME" + echo -e "\n$DESCRIPTION\n$TABLE" >> "tests/results/$FILE_NAME" # shellcheck disable=SC2016,SC2128 # Irrelevant echo -e '
Run details From c5f2a618a83f04113a3a27475663a709e2c83f5f Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Thu, 6 Jan 2022 17:09:51 +0200 Subject: [PATCH 202/214] chore: Improved code structure (moved hooks into a separate dir) (#316) --- .pre-commit-hooks.yaml | 24 +-- {pre_commit_hooks => hooks}/__init__.py | 0 terragrunt_fmt.sh => hooks/_common.sh | 50 +++---- .../infracost_breakdown.sh | 71 +-------- terraform_docs.sh => hooks/terraform_docs.sh | 50 ++----- .../terraform_docs_replace.py | 0 terraform_fmt.sh => hooks/terraform_fmt.sh | 48 +----- hooks/terraform_providers_lock.sh | 42 ++++++ hooks/terraform_tflint.sh | 39 +++++ hooks/terraform_tfsec.sh | 34 +++++ .../terraform_validate.sh | 19 +-- hooks/terragrunt_fmt.sh | 31 ++++ hooks/terragrunt_validate.sh | 31 ++++ hooks/terrascan.sh | 31 ++++ setup.py | 2 +- terraform_providers_lock.sh | 141 ------------------ terraform_tflint.sh | 137 ----------------- terraform_tfsec.sh | 109 -------------- terragrunt_validate.sh | 107 ------------- terrascan.sh | 107 ------------- 20 files changed, 274 insertions(+), 799 deletions(-) rename {pre_commit_hooks => hooks}/__init__.py (100%) rename terragrunt_fmt.sh => hooks/_common.sh (71%) mode change 100755 => 100644 rename infracost_breakdown.sh => hooks/infracost_breakdown.sh (76%) rename terraform_docs.sh => hooks/terraform_docs.sh (92%) rename {pre_commit_hooks => hooks}/terraform_docs_replace.py (100%) rename terraform_fmt.sh => hooks/terraform_fmt.sh (71%) create mode 100755 hooks/terraform_providers_lock.sh create mode 100755 hooks/terraform_tflint.sh create mode 100755 hooks/terraform_tfsec.sh rename terraform_validate.sh => hooks/terraform_validate.sh (88%) create mode 100755 hooks/terragrunt_fmt.sh create mode 100755 hooks/terragrunt_validate.sh create mode 100755 hooks/terrascan.sh delete mode 100755 terraform_providers_lock.sh delete mode 100755 terraform_tflint.sh delete mode 100755 terraform_tfsec.sh delete mode 100755 terragrunt_validate.sh delete mode 100755 terrascan.sh diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 95a000ffb..53004bfc9 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -1,7 +1,7 @@ - id: infracost_breakdown name: Infracost breakdown description: Check terraform infrastructure cost - entry: infracost_breakdown.sh + entry: hooks/infracost_breakdown.sh language: script require_serial: true files: \.(tf(vars)?|hcl)$ @@ -10,7 +10,7 @@ - id: terraform_fmt name: Terraform fmt description: Rewrites all Terraform configuration files to a canonical format. - entry: terraform_fmt.sh + entry: hooks/terraform_fmt.sh language: script files: (\.tf|\.tfvars)$ exclude: \.terraform\/.*$ @@ -19,7 +19,7 @@ name: Terraform docs description: Inserts input and output documentation into README.md (using terraform-docs). require_serial: true - entry: terraform_docs.sh + entry: hooks/terraform_docs.sh language: script files: (\.tf|\.terraform\.lock\.hcl)$ exclude: \.terraform\/.*$ @@ -28,7 +28,7 @@ name: Terraform docs (without aggregate type defaults) description: Inserts input and output documentation into README.md (using terraform-docs). Identical to terraform_docs. require_serial: true - entry: terraform_docs.sh + entry: hooks/terraform_docs.sh language: script files: (\.tf)$ exclude: \.terraform\/.*$ @@ -46,7 +46,7 @@ name: Terraform validate description: Validates all Terraform configuration files. require_serial: true - entry: terraform_validate.sh + entry: hooks/terraform_validate.sh language: script files: (\.tf|\.tfvars)$ exclude: \.terraform\/.*$ @@ -55,7 +55,7 @@ name: Lock terraform provider versions description: Updates provider signatures in dependency lock files. require_serial: true - entry: terraform_providers_lock.sh + entry: hooks/terraform_providers_lock.sh language: script files: (\.terraform\.lock\.hcl)$ exclude: \.terraform\/.*$ @@ -64,7 +64,7 @@ name: Terraform validate with tflint description: Validates all Terraform configuration files with TFLint. require_serial: true - entry: terraform_tflint.sh + entry: hooks/terraform_tflint.sh language: script files: (\.tf|\.tfvars)$ exclude: \.terraform\/.*$ @@ -72,7 +72,7 @@ - id: terragrunt_fmt name: Terragrunt fmt description: Rewrites all Terragrunt configuration files to a canonical format. - entry: terragrunt_fmt.sh + entry: hooks/terragrunt_fmt.sh language: script files: (\.hcl)$ exclude: \.terraform\/.*$ @@ -80,7 +80,7 @@ - id: terragrunt_validate name: Terragrunt validate description: Validates all Terragrunt configuration files. - entry: terragrunt_validate.sh + entry: hooks/terragrunt_validate.sh language: script files: (\.hcl)$ exclude: \.terraform\/.*$ @@ -89,13 +89,13 @@ name: Terraform validate with tfsec description: Static analysis of Terraform templates to spot potential security issues. require_serial: true - entry: terraform_tfsec.sh + entry: hooks/terraform_tfsec.sh language: script - id: checkov name: Checkov description: Runs checkov on Terraform templates. - entry: checkov -d . + entry: hooks/checkov -d . language: python pass_filenames: false always_run: false @@ -107,7 +107,7 @@ name: terrascan description: Runs terrascan on Terraform templates. language: script - entry: terrascan.sh + entry: hooks/terrascan.sh files: \.tf$ exclude: \.terraform\/.*$ require_serial: true diff --git a/pre_commit_hooks/__init__.py b/hooks/__init__.py similarity index 100% rename from pre_commit_hooks/__init__.py rename to hooks/__init__.py diff --git a/terragrunt_fmt.sh b/hooks/_common.sh old mode 100755 new mode 100644 similarity index 71% rename from terragrunt_fmt.sh rename to hooks/_common.sh index 42f9b5ed5..a835b5d18 --- a/terragrunt_fmt.sh +++ b/hooks/_common.sh @@ -1,26 +1,17 @@ #!/usr/bin/env bash set -eo pipefail -function main { - common::initialize - common::parse_cmdline "$@" - common::per_dir_hook "${ARGS[*]}" "${FILES[@]}" -} - function common::initialize { - local SCRIPT_DIR - # get directory containing this script - SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" - + local -r script_dir=$1 # source getopt function # shellcheck source=lib_getopt - . "$SCRIPT_DIR/lib_getopt" + . "$script_dir/../lib_getopt" } function common::parse_cmdline { # common global arrays. # Populated via `common::parse_cmdline` and can be used inside hooks' functions - declare -g -a ARGS=() FILES=() HOOK_CONFIG=() + declare -g -a ARGS=() HOOK_CONFIG=() FILES=() local argv argv=$(getopt -o a:,h: --long args:,hook-config: -- "$@") || return @@ -40,6 +31,7 @@ function common::parse_cmdline { ;; --) shift + # shellcheck disable=SC2034 # Variable is used FILES=("$@") break ;; @@ -90,18 +82,24 @@ function common::per_dir_hook { exit $final_exit_code } -function per_dir_hook_unique_part { - # common logic located in common::per_dir_hook - local -r args="$1" - local -r dir_path="$2" - - # pass the arguments to hook - # shellcheck disable=SC2068 # hook fails when quoting is used ("$arg[@]") - terragrunt hclfmt ${args[@]} - - # return exit code to common::per_dir_hook - local exit_code=$? - return $exit_code +function common::colorify { + # shellcheck disable=SC2034 + local -r red="\e[0m\e[31m" + # shellcheck disable=SC2034 + local -r green="\e[0m\e[32m" + # shellcheck disable=SC2034 + local -r yellow="\e[0m\e[33m" + # Color reset + local -r RESET="\e[0m" + + # Params start # + local COLOR="${!1}" + local -r TEXT=$2 + # Params end # + + if [ "$PRE_COMMIT_COLOR" = "never" ]; then + COLOR=$RESET + fi + + echo -e "${COLOR}${TEXT}${RESET}" } - -[ "${BASH_SOURCE[0]}" != "$0" ] || main "$@" diff --git a/infracost_breakdown.sh b/hooks/infracost_breakdown.sh similarity index 76% rename from infracost_breakdown.sh rename to hooks/infracost_breakdown.sh index f2cb816dd..911bcacaf 100755 --- a/infracost_breakdown.sh +++ b/hooks/infracost_breakdown.sh @@ -1,75 +1,18 @@ #!/usr/bin/env bash set -eo pipefail +# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines +readonly SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" +# shellcheck source=_common.sh +. "$SCRIPT_DIR/_common.sh" + function main { - common::initialize + common::initialize "$SCRIPT_DIR" common::parse_cmdline "$@" + # shellcheck disable=SC2153 # False positive infracost_breakdown_ "${HOOK_CONFIG[*]}" "${ARGS[*]}" } -function common::colorify { - # shellcheck disable=SC2034 - local -r red="\e[0m\e[31m" - # shellcheck disable=SC2034 - local -r green="\e[0m\e[32m" - # shellcheck disable=SC2034 - local -r yellow="\e[0m\e[33m" - # Color reset - local -r RESET="\e[0m" - - # Params start # - local COLOR="${!1}" - local -r TEXT=$2 - # Params end # - - if [ "$PRE_COMMIT_COLOR" = "never" ]; then - COLOR=$RESET - fi - - echo -e "${COLOR}${TEXT}${RESET}" -} - -function common::initialize { - local SCRIPT_DIR - # get directory containing this script - SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" - - # source getopt function - # shellcheck source=lib_getopt - . "$SCRIPT_DIR/lib_getopt" -} - -function common::parse_cmdline { - # common global arrays. - # Populated via `common::parse_cmdline` and can be used inside hooks' functions - declare -g -a ARGS=() FILES=() HOOK_CONFIG=() - - local argv - argv=$(getopt -o a:,h: --long args:,hook-config: -- "$@") || return - eval "set -- $argv" - - for argv; do - case $argv in - -a | --args) - shift - ARGS+=("$1") - shift - ;; - -h | --hook-config) - shift - HOOK_CONFIG+=("$1;") - shift - ;; - --) - shift - # shellcheck disable=SC2034 # Common function - FILES=("$@") - break - ;; - esac - done -} - function infracost_breakdown_ { local -r hook_config="$1" local args diff --git a/terraform_docs.sh b/hooks/terraform_docs.sh similarity index 92% rename from terraform_docs.sh rename to hooks/terraform_docs.sh index f2efb0af7..b1757c343 100755 --- a/terraform_docs.sh +++ b/hooks/terraform_docs.sh @@ -1,54 +1,22 @@ #!/usr/bin/env bash set -eo pipefail +# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines +readonly SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" +# shellcheck source=_common.sh +. "$SCRIPT_DIR/_common.sh" + function main { - common::initialize + common::initialize "$SCRIPT_DIR" common::parse_cmdline "$@" # Support for setting relative PATH to .terraform-docs.yml config. + # shellcheck disable=SC2178 # It's the simplest syntax for that case ARGS=${ARGS[*]/--config=/--config=$(pwd)\/} + # shellcheck disable=SC2128 # It's the simplest syntax for that case + # shellcheck disable=SC2153 # False positive terraform_docs_ "${HOOK_CONFIG[*]}" "$ARGS" "${FILES[@]}" } -function common::initialize { - local SCRIPT_DIR - # get directory containing this script - SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" - - # source getopt function - # shellcheck source=lib_getopt - . "$SCRIPT_DIR/lib_getopt" -} - -function common::parse_cmdline { - # common global arrays. - # Populated via `common::parse_cmdline` and can be used inside hooks' functions - declare -g -a ARGS=() FILES=() HOOK_CONFIG=() - - local argv - argv=$(getopt -o a:,h: --long args:,hook-config: -- "$@") || return - eval "set -- $argv" - - for argv; do - case $argv in - -a | --args) - shift - ARGS+=("$1") - shift - ;; - -h | --hook-config) - shift - HOOK_CONFIG+=("$1;") - shift - ;; - --) - shift - FILES=("$@") - break - ;; - esac - done -} - function terraform_docs_ { local -r hook_config="$1" local -r args="$2" diff --git a/pre_commit_hooks/terraform_docs_replace.py b/hooks/terraform_docs_replace.py similarity index 100% rename from pre_commit_hooks/terraform_docs_replace.py rename to hooks/terraform_docs_replace.py diff --git a/terraform_fmt.sh b/hooks/terraform_fmt.sh similarity index 71% rename from terraform_fmt.sh rename to hooks/terraform_fmt.sh index f6cad78c8..9657af2dc 100755 --- a/terraform_fmt.sh +++ b/hooks/terraform_fmt.sh @@ -1,52 +1,18 @@ #!/usr/bin/env bash set -eo pipefail +# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines +readonly SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" +# shellcheck source=_common.sh +. "$SCRIPT_DIR/_common.sh" + function main { - common::initialize + common::initialize "$SCRIPT_DIR" common::parse_cmdline "$@" + # shellcheck disable=SC2153 # False positive terraform_fmt_ "${ARGS[*]}" "${FILES[@]}" } -function common::initialize { - local SCRIPT_DIR - # get directory containing this script - SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" - - # source getopt function - # shellcheck source=lib_getopt - . "$SCRIPT_DIR/lib_getopt" -} - -function common::parse_cmdline { - # common global arrays. - # Populated via `common::parse_cmdline` and can be used inside hooks' functions - declare -g -a ARGS=() FILES=() HOOK_CONFIG=() - - local argv - argv=$(getopt -o a:,h: --long args:,hook-config: -- "$@") || return - eval "set -- $argv" - - for argv; do - case $argv in - -a | --args) - shift - ARGS+=("$1") - shift - ;; - -h | --hook-config) - shift - HOOK_CONFIG+=("$1;") - shift - ;; - --) - shift - FILES=("$@") - break - ;; - esac - done -} - function terraform_fmt_ { local -r args="$1" shift 1 diff --git a/hooks/terraform_providers_lock.sh b/hooks/terraform_providers_lock.sh new file mode 100755 index 000000000..b5fb7ec31 --- /dev/null +++ b/hooks/terraform_providers_lock.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash + +set -eo pipefail + +# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines +readonly SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" +# shellcheck source=_common.sh +. "$SCRIPT_DIR/_common.sh" + +function main { + common::initialize "$SCRIPT_DIR" + common::parse_cmdline "$@" + # shellcheck disable=SC2153 # False positive + common::per_dir_hook "${ARGS[*]}" "${FILES[@]}" +} + +function per_dir_hook_unique_part { + # common logic located in common::per_dir_hook + local -r args="$1" + local -r dir_path="$2" + + if [ ! -d ".terraform" ]; then + init_output=$(terraform init -backend=false 2>&1) + init_code=$? + + if [ $init_code -ne 0 ]; then + common::colorify "red" "Init before validation failed: $dir_path" + common::colorify "red" "$init_output" + exit $init_code + fi + fi + + # pass the arguments to hook + # shellcheck disable=SC2068 # hook fails when quoting is used ("$arg[@]") + terraform providers lock ${args[@]} + + # return exit code to common::per_dir_hook + local exit_code=$? + return $exit_code +} + +[ "${BASH_SOURCE[0]}" != "$0" ] || main "$@" diff --git a/hooks/terraform_tflint.sh b/hooks/terraform_tflint.sh new file mode 100755 index 000000000..6d154c50b --- /dev/null +++ b/hooks/terraform_tflint.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash + +set -eo pipefail + +# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines +readonly SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" +# shellcheck source=_common.sh +. "$SCRIPT_DIR/_common.sh" + +function main { + common::initialize "$SCRIPT_DIR" + common::parse_cmdline "$@" + # Support for setting PATH to repo root. + # shellcheck disable=SC2178 # It's the simplest syntax for that case + ARGS=${ARGS[*]/__GIT_WORKING_DIR__/$(pwd)\/} + # shellcheck disable=SC2128 # It's the simplest syntax for that case + common::per_dir_hook "$ARGS" "${FILES[@]}" +} + +function per_dir_hook_unique_part { + # common logic located in common::per_dir_hook + local -r args="$1" + local -r dir_path="$2" + + # Print checked PATH **only** if TFLint have any messages + # shellcheck disable=SC2091,SC2068 # Suppress error output + $(tflint ${args[@]} 2>&1) 2> /dev/null || { + common::colorify "yellow" "TFLint in $dir_path/:" + + # shellcheck disable=SC2068 # hook fails when quoting is used ("$arg[@]") + tflint ${args[@]} + } + + # return exit code to common::per_dir_hook + local exit_code=$? + return $exit_code +} + +[ "${BASH_SOURCE[0]}" != "$0" ] || main "$@" diff --git a/hooks/terraform_tfsec.sh b/hooks/terraform_tfsec.sh new file mode 100755 index 000000000..284106bfa --- /dev/null +++ b/hooks/terraform_tfsec.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +set -eo pipefail + +# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines +readonly SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" +# shellcheck source=_common.sh +. "$SCRIPT_DIR/_common.sh" + +function main { + common::initialize "$SCRIPT_DIR" + common::parse_cmdline "$@" + # Support for setting PATH to repo root. + # shellcheck disable=SC2178 # It's the simplest syntax for that case + ARGS=${ARGS[*]/__GIT_WORKING_DIR__/$(pwd)\/} + # shellcheck disable=SC2128 # It's the simplest syntax for that case + common::per_dir_hook "$ARGS" "${FILES[@]}" +} + +function per_dir_hook_unique_part { + # common logic located in common::per_dir_hook + local -r args="$1" + # shellcheck disable=SC2034 # Unused var. + local -r dir_path="$2" + + # pass the arguments to hook + # shellcheck disable=SC2068 # hook fails when quoting is used ("$arg[@]") + tfsec ${args[@]} + + # return exit code to common::per_dir_hook + local exit_code=$? + return $exit_code +} + +[ "${BASH_SOURCE[0]}" != "$0" ] || main "$@" diff --git a/terraform_validate.sh b/hooks/terraform_validate.sh similarity index 88% rename from terraform_validate.sh rename to hooks/terraform_validate.sh index 655bc09ac..4cb50c946 100755 --- a/terraform_validate.sh +++ b/hooks/terraform_validate.sh @@ -1,25 +1,20 @@ #!/usr/bin/env bash set -eo pipefail +# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines +readonly SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" +# shellcheck source=_common.sh +. "$SCRIPT_DIR/_common.sh" + # `terraform validate` requires this env variable to be set export AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION:-us-east-1} function main { - common::initialize + common::initialize "$SCRIPT_DIR" parse_cmdline_ "$@" terraform_validate_ } -function common::initialize { - local SCRIPT_DIR - # get directory containing this script - SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" - - # source getopt function - # shellcheck source=lib_getopt - . "$SCRIPT_DIR/lib_getopt" -} - function parse_cmdline_ { declare argv argv=$(getopt -o e:i:a: --long envs:,init-args:,args: -- "$@") || return @@ -119,9 +114,7 @@ function terraform_validate_ { } # global arrays -declare -a ARGS declare -a INIT_ARGS declare -a ENVS -declare -a FILES [ "${BASH_SOURCE[0]}" != "$0" ] || main "$@" diff --git a/hooks/terragrunt_fmt.sh b/hooks/terragrunt_fmt.sh new file mode 100755 index 000000000..d91cc9fcc --- /dev/null +++ b/hooks/terragrunt_fmt.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +set -eo pipefail + +# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines +readonly SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" +# shellcheck source=_common.sh +. "$SCRIPT_DIR/_common.sh" + +function main { + common::initialize "$SCRIPT_DIR" + common::parse_cmdline "$@" + # shellcheck disable=SC2153 # False positive + common::per_dir_hook "${ARGS[*]}" "${FILES[@]}" +} + +function per_dir_hook_unique_part { + # common logic located in common::per_dir_hook + local -r args="$1" + # shellcheck disable=SC2034 # Unused var. + local -r dir_path="$2" + + # pass the arguments to hook + # shellcheck disable=SC2068 # hook fails when quoting is used ("$arg[@]") + terragrunt hclfmt ${args[@]} + + # return exit code to common::per_dir_hook + local exit_code=$? + return $exit_code +} + +[ "${BASH_SOURCE[0]}" != "$0" ] || main "$@" diff --git a/hooks/terragrunt_validate.sh b/hooks/terragrunt_validate.sh new file mode 100755 index 000000000..96fe3963d --- /dev/null +++ b/hooks/terragrunt_validate.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +set -eo pipefail + +# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines +readonly SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" +# shellcheck source=_common.sh +. "$SCRIPT_DIR/_common.sh" + +function main { + common::initialize "$SCRIPT_DIR" + common::parse_cmdline "$@" + # shellcheck disable=SC2153 # False positive + common::per_dir_hook "${ARGS[*]}" "${FILES[@]}" +} + +function per_dir_hook_unique_part { + # common logic located in common::per_dir_hook + local -r args="$1" + # shellcheck disable=SC2034 # Unused var. + local -r dir_path="$2" + + # pass the arguments to hook + # shellcheck disable=SC2068 # hook fails when quoting is used ("$arg[@]") + terragrunt validate ${args[@]} + + # return exit code to common::per_dir_hook + local exit_code=$? + return $exit_code +} + +[ "${BASH_SOURCE[0]}" != "$0" ] || main "$@" diff --git a/hooks/terrascan.sh b/hooks/terrascan.sh new file mode 100755 index 000000000..3bf78c7d2 --- /dev/null +++ b/hooks/terrascan.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +set -eo pipefail + +# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines +readonly SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" +# shellcheck source=_common.sh +. "$SCRIPT_DIR/_common.sh" + +function main { + common::initialize "$SCRIPT_DIR" + common::parse_cmdline "$@" + # shellcheck disable=SC2153 # False positive + common::per_dir_hook "${ARGS[*]}" "${FILES[@]}" +} + +function per_dir_hook_unique_part { + # common logic located in common::per_dir_hook + local -r args="$1" + # shellcheck disable=SC2034 # Unused var. + local -r dir_path="$2" + + # pass the arguments to hook + # shellcheck disable=SC2068 # hook fails when quoting is used ("$arg[@]") + terrascan scan -i terraform ${args[@]} + + # return exit code to common::per_dir_hook + local exit_code=$? + return $exit_code +} + +[ "${BASH_SOURCE[0]}" != "$0" ] || main "$@" diff --git a/setup.py b/setup.py index 4c2b47668..2d88425b9 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ ], entry_points={ 'console_scripts': [ - 'terraform_docs_replace = pre_commit_hooks.terraform_docs_replace:main', + 'terraform_docs_replace = hooks.terraform_docs_replace:main', ], }, ) diff --git a/terraform_providers_lock.sh b/terraform_providers_lock.sh deleted file mode 100755 index 26bf40b58..000000000 --- a/terraform_providers_lock.sh +++ /dev/null @@ -1,141 +0,0 @@ -#!/usr/bin/env bash - -set -eo pipefail - -function main { - common::initialize - common::parse_cmdline "$@" - common::per_dir_hook "${ARGS[*]}" "${FILES[@]}" -} - -function common::colorify { - # shellcheck disable=SC2034 - local -r red="\e[0m\e[31m" - # shellcheck disable=SC2034 - local -r green="\e[0m\e[32m" - # shellcheck disable=SC2034 - local -r yellow="\e[0m\e[33m" - # Color reset - local -r RESET="\e[0m" - - # Params start # - local COLOR="${!1}" - local -r TEXT=$2 - # Params end # - - if [ "$PRE_COMMIT_COLOR" = "never" ]; then - COLOR=$RESET - fi - - echo -e "${COLOR}${TEXT}${RESET}" -} - -function common::initialize { - local SCRIPT_DIR - # get directory containing this script - SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" - - # source getopt function - # shellcheck source=lib_getopt - . "$SCRIPT_DIR/lib_getopt" -} - -function common::parse_cmdline { - # common global arrays. - # Populated via `common::parse_cmdline` and can be used inside hooks' functions - declare -g -a ARGS=() FILES=() HOOK_CONFIG=() - - local argv - argv=$(getopt -o a:,h: --long args:,hook-config: -- "$@") || return - eval "set -- $argv" - - for argv; do - case $argv in - -a | --args) - shift - ARGS+=("$1") - shift - ;; - -h | --hook-config) - shift - HOOK_CONFIG+=("$1;") - shift - ;; - --) - shift - FILES=("$@") - break - ;; - esac - done -} - -function common::per_dir_hook { - local -r args="$1" - shift 1 - local -a -r files=("$@") - - # consume modified files passed from pre-commit so that - # hook runs against only those relevant directories - local index=0 - for file_with_path in "${files[@]}"; do - file_with_path="${file_with_path// /__REPLACED__SPACE__}" - - dir_paths[index]=$(dirname "$file_with_path") - - ((index += 1)) - done - - # preserve errexit status - shopt -qo errexit && ERREXIT_IS_SET=true - # allow hook to continue if exit_code is greater than 0 - set +e - local final_exit_code=0 - - # run hook for each path - for dir_path in $(echo "${dir_paths[*]}" | tr ' ' '\n' | sort -u); do - dir_path="${dir_path//__REPLACED__SPACE__/ }" - pushd "$dir_path" > /dev/null || continue - - per_dir_hook_unique_part "$args" "$dir_path" - - local exit_code=$? - if [ $exit_code -ne 0 ]; then - final_exit_code=$exit_code - fi - - popd > /dev/null - done - - # restore errexit if it was set before the "for" loop - [[ $ERREXIT_IS_SET ]] && set -e - # return the hook final exit_code - exit $final_exit_code -} - -function per_dir_hook_unique_part { - # common logic located in common::per_dir_hook - local -r args="$1" - local -r dir_path="$2" - - if [ ! -d ".terraform" ]; then - init_output=$(terraform init -backend=false 2>&1) - init_code=$? - - if [ $init_code -ne 0 ]; then - common::colorify "red" "Init before validation failed: $dir_path" - common::colorify "red" "$init_output" - exit $init_code - fi - fi - - # pass the arguments to hook - # shellcheck disable=SC2068 # hook fails when quoting is used ("$arg[@]") - terraform providers lock ${args[@]} - - # return exit code to common::per_dir_hook - local exit_code=$? - return $exit_code -} - -[ "${BASH_SOURCE[0]}" != "$0" ] || main "$@" diff --git a/terraform_tflint.sh b/terraform_tflint.sh deleted file mode 100755 index d6501b84f..000000000 --- a/terraform_tflint.sh +++ /dev/null @@ -1,137 +0,0 @@ -#!/usr/bin/env bash - -set -eo pipefail - -function main { - common::initialize - common::parse_cmdline "$@" - # Support for setting PATH to repo root. - ARGS=${ARGS[*]/__GIT_WORKING_DIR__/$(pwd)\/} - common::per_dir_hook "$ARGS" "${FILES[@]}" -} - -function common::colorify { - # shellcheck disable=SC2034 - local -r red="\e[0m\e[31m" - # shellcheck disable=SC2034 - local -r green="\e[0m\e[32m" - # shellcheck disable=SC2034 - local -r yellow="\e[0m\e[33m" - # Color reset - local -r RESET="\e[0m" - - # Params start # - local COLOR="${!1}" - local -r TEXT=$2 - # Params end # - - if [ "$PRE_COMMIT_COLOR" = "never" ]; then - COLOR=$RESET - fi - - echo -e "${COLOR}${TEXT}${RESET}" -} - -function common::initialize { - local SCRIPT_DIR - # get directory containing this script - SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" - - # source getopt function - # shellcheck source=lib_getopt - . "$SCRIPT_DIR/lib_getopt" -} - -function common::parse_cmdline { - # common global arrays. - # Populated via `common::parse_cmdline` and can be used inside hooks' functions - declare -g -a ARGS=() FILES=() HOOK_CONFIG=() - - local argv - argv=$(getopt -o a:,h: --long args:,hook-config: -- "$@") || return - eval "set -- $argv" - - for argv; do - case $argv in - -a | --args) - shift - ARGS+=("$1") - shift - ;; - -h | --hook-config) - shift - HOOK_CONFIG+=("$1;") - shift - ;; - --) - shift - FILES=("$@") - break - ;; - esac - done -} - -function common::per_dir_hook { - local -r args="$1" - shift 1 - local -a -r files=("$@") - - # consume modified files passed from pre-commit so that - # hook runs against only those relevant directories - local index=0 - for file_with_path in "${files[@]}"; do - file_with_path="${file_with_path// /__REPLACED__SPACE__}" - - dir_paths[index]=$(dirname "$file_with_path") - - ((index += 1)) - done - - # preserve errexit status - shopt -qo errexit && ERREXIT_IS_SET=true - # allow hook to continue if exit_code is greater than 0 - set +e - local final_exit_code=0 - - # run hook for each path - for dir_path in $(echo "${dir_paths[*]}" | tr ' ' '\n' | sort -u); do - dir_path="${dir_path//__REPLACED__SPACE__/ }" - pushd "$dir_path" > /dev/null || continue - - per_dir_hook_unique_part "$args" "$dir_path" - - local exit_code=$? - if [ $exit_code -ne 0 ]; then - final_exit_code=$exit_code - fi - - popd > /dev/null - done - - # restore errexit if it was set before the "for" loop - [[ $ERREXIT_IS_SET ]] && set -e - # return the hook final exit_code - exit $final_exit_code -} - -function per_dir_hook_unique_part { - # common logic located in common::per_dir_hook - local -r args="$1" - local -r dir_path="$2" - - # Print checked PATH **only** if TFLint have any messages - # shellcheck disable=SC2091,SC2068 # Suppress error output - $(tflint ${args[@]} 2>&1) 2> /dev/null || { - common::colorify "yellow" "TFLint in $dir_path/:" - - # shellcheck disable=SC2068 # hook fails when quoting is used ("$arg[@]") - tflint ${args[@]} - } - - # return exit code to common::per_dir_hook - local exit_code=$? - return $exit_code -} - -[ "${BASH_SOURCE[0]}" != "$0" ] || main "$@" diff --git a/terraform_tfsec.sh b/terraform_tfsec.sh deleted file mode 100755 index a68905bca..000000000 --- a/terraform_tfsec.sh +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/env bash -set -eo pipefail - -function main { - common::initialize - common::parse_cmdline "$@" - # Support for setting PATH to repo root. - ARGS=${ARGS[*]/__GIT_WORKING_DIR__/$(pwd)\/} - common::per_dir_hook "$ARGS" "${FILES[@]}" -} - -function common::initialize { - local SCRIPT_DIR - # get directory containing this script - SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" - - # source getopt function - # shellcheck source=lib_getopt - . "$SCRIPT_DIR/lib_getopt" -} - -function common::parse_cmdline { - # common global arrays. - # Populated via `common::parse_cmdline` and can be used inside hooks' functions - declare -g -a ARGS=() FILES=() HOOK_CONFIG=() - - local argv - argv=$(getopt -o a:,h: --long args:,hook-config: -- "$@") || return - eval "set -- $argv" - - for argv; do - case $argv in - -a | --args) - shift - ARGS+=("$1") - shift - ;; - -h | --hook-config) - shift - HOOK_CONFIG+=("$1;") - shift - ;; - --) - shift - FILES=("$@") - break - ;; - esac - done -} - -function common::per_dir_hook { - local -r args="$1" - shift 1 - local -a -r files=("$@") - - # consume modified files passed from pre-commit so that - # hook runs against only those relevant directories - local index=0 - for file_with_path in "${files[@]}"; do - file_with_path="${file_with_path// /__REPLACED__SPACE__}" - - dir_paths[index]=$(dirname "$file_with_path") - - ((index += 1)) - done - - # preserve errexit status - shopt -qo errexit && ERREXIT_IS_SET=true - # allow hook to continue if exit_code is greater than 0 - set +e - local final_exit_code=0 - - # run hook for each path - for dir_path in $(echo "${dir_paths[*]}" | tr ' ' '\n' | sort -u); do - dir_path="${dir_path//__REPLACED__SPACE__/ }" - pushd "$dir_path" > /dev/null || continue - - per_dir_hook_unique_part "$args" "$dir_path" - - local exit_code=$? - if [ $exit_code -ne 0 ]; then - final_exit_code=$exit_code - fi - - popd > /dev/null - done - - # restore errexit if it was set before the "for" loop - [[ $ERREXIT_IS_SET ]] && set -e - # return the hook final exit_code - exit $final_exit_code -} - -function per_dir_hook_unique_part { - # common logic located in common::per_dir_hook - local -r args="$1" - local -r dir_path="$2" - - # pass the arguments to hook - # shellcheck disable=SC2068 # hook fails when quoting is used ("$arg[@]") - tfsec ${args[@]} - - # return exit code to common::per_dir_hook - local exit_code=$? - return $exit_code -} - -[ "${BASH_SOURCE[0]}" != "$0" ] || main "$@" diff --git a/terragrunt_validate.sh b/terragrunt_validate.sh deleted file mode 100755 index 1fd83a1dc..000000000 --- a/terragrunt_validate.sh +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/env bash -set -eo pipefail - -function main { - common::initialize - common::parse_cmdline "$@" - common::per_dir_hook "${ARGS[*]}" "${FILES[@]}" -} - -function common::initialize { - local SCRIPT_DIR - # get directory containing this script - SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" - - # source getopt function - # shellcheck source=lib_getopt - . "$SCRIPT_DIR/lib_getopt" -} - -function common::parse_cmdline { - # common global arrays. - # Populated via `common::parse_cmdline` and can be used inside hooks' functions - declare -g -a ARGS=() FILES=() HOOK_CONFIG=() - - local argv - argv=$(getopt -o a:,h: --long args:,hook-config: -- "$@") || return - eval "set -- $argv" - - for argv; do - case $argv in - -a | --args) - shift - ARGS+=("$1") - shift - ;; - -h | --hook-config) - shift - HOOK_CONFIG+=("$1;") - shift - ;; - --) - shift - FILES=("$@") - break - ;; - esac - done -} - -function common::per_dir_hook { - local -r args="$1" - shift 1 - local -a -r files=("$@") - - # consume modified files passed from pre-commit so that - # hook runs against only those relevant directories - local index=0 - for file_with_path in "${files[@]}"; do - file_with_path="${file_with_path// /__REPLACED__SPACE__}" - - dir_paths[index]=$(dirname "$file_with_path") - - ((index += 1)) - done - - # preserve errexit status - shopt -qo errexit && ERREXIT_IS_SET=true - # allow hook to continue if exit_code is greater than 0 - set +e - local final_exit_code=0 - - # run hook for each path - for dir_path in $(echo "${dir_paths[*]}" | tr ' ' '\n' | sort -u); do - dir_path="${dir_path//__REPLACED__SPACE__/ }" - pushd "$dir_path" > /dev/null || continue - - per_dir_hook_unique_part "$args" "$dir_path" - - local exit_code=$? - if [ $exit_code -ne 0 ]; then - final_exit_code=$exit_code - fi - - popd > /dev/null - done - - # restore errexit if it was set before the "for" loop - [[ $ERREXIT_IS_SET ]] && set -e - # return the hook final exit_code - exit $final_exit_code -} - -function per_dir_hook_unique_part { - # common logic located in common::per_dir_hook - local -r args="$1" - local -r dir_path="$2" - - # pass the arguments to hook - # shellcheck disable=SC2068 # hook fails when quoting is used ("$arg[@]") - terragrunt validate ${args[@]} - - # return exit code to common::per_dir_hook - local exit_code=$? - return $exit_code -} - -[ "${BASH_SOURCE[0]}" != "$0" ] || main "$@" diff --git a/terrascan.sh b/terrascan.sh deleted file mode 100755 index 1ed33bc4a..000000000 --- a/terrascan.sh +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/env bash -set -eo pipefail - -function main { - common::initialize - common::parse_cmdline "$@" - common::per_dir_hook "${ARGS[*]}" "${FILES[@]}" -} - -function common::initialize { - local SCRIPT_DIR - # get directory containing this script - SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" - - # source getopt function - # shellcheck source=lib_getopt - . "$SCRIPT_DIR/lib_getopt" -} - -function common::parse_cmdline { - # common global arrays. - # Populated via `common::parse_cmdline` and can be used inside hooks' functions - declare -g -a ARGS=() FILES=() HOOK_CONFIG=() - - local argv - argv=$(getopt -o a:,h: --long args:,hook-config: -- "$@") || return - eval "set -- $argv" - - for argv; do - case $argv in - -a | --args) - shift - ARGS+=("$1") - shift - ;; - -h | --hook-config) - shift - HOOK_CONFIG+=("$1;") - shift - ;; - --) - shift - FILES=("$@") - break - ;; - esac - done -} - -function common::per_dir_hook { - local -r args="$1" - shift 1 - local -a -r files=("$@") - - # consume modified files passed from pre-commit so that - # hook runs against only those relevant directories - local index=0 - for file_with_path in "${files[@]}"; do - file_with_path="${file_with_path// /__REPLACED__SPACE__}" - - dir_paths[index]=$(dirname "$file_with_path") - - ((index += 1)) - done - - # preserve errexit status - shopt -qo errexit && ERREXIT_IS_SET=true - # allow hook to continue if exit_code is greater than 0 - set +e - local final_exit_code=0 - - # run hook for each path - for dir_path in $(echo "${dir_paths[*]}" | tr ' ' '\n' | sort -u); do - dir_path="${dir_path//__REPLACED__SPACE__/ }" - pushd "$dir_path" > /dev/null || continue - - per_dir_hook_unique_part "$args" "$dir_path" - - local exit_code=$? - if [ $exit_code -ne 0 ]; then - final_exit_code=$exit_code - fi - - popd > /dev/null - done - - # restore errexit if it was set before the "for" loop - [[ $ERREXIT_IS_SET ]] && set -e - # return the hook final exit_code - exit $final_exit_code -} - -function per_dir_hook_unique_part { - # common logic located in common::per_dir_hook - local -r args="$1" - local -r dir_path="$2" - - # pass the arguments to hook - # shellcheck disable=SC2068 # hook fails when quoting is used ("$arg[@]") - terrascan scan -i terraform ${args[@]} - - # return exit code to common::per_dir_hook - local exit_code=$? - return $exit_code -} - -[ "${BASH_SOURCE[0]}" != "$0" ] || main "$@" From 661a0cf3463510156b388b18ef7a0c49db2f4ae9 Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Tue, 11 Jan 2022 13:12:29 +0200 Subject: [PATCH 203/214] chore: Specify what we exactly mean (#320) --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index a7af18c5b..bd5e5c69f 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -22,7 +22,7 @@ can uncomment the below line to indicate which issue your PR fixes, for example -### How has this code been tested +### How can we test changes