Git-hooks - a practical example with tf docs

Posted in development on September 27, 2023 by Adrian Wyssmann ‐ 3 min read

You are working on terraform code and want to ensure your documentation is updated after you made some changes. Why don't you use git-hooks for that?

A long time ago I wrote about git-hooks, today I want to give you a practicle example on how I ensure my terraform documetation stays up-to-date.

terraform-docs

Not sure if you every heard of terraform-docs, but it’s a tool which generates terraform modules documentation in various formats. You can define a config file .terraform-docs.yml which defines how your documentations shall look like. My $HOME\.terraform-docs.yml looks as follows:

formatter: "markdown" # this is required

sections:
  hide: []
  show: []

content: |-
  {{ .Inputs }}
  {{ .Outputs }}
  {{ .Resources }}  

output:
  file: "README.md"
  mode: inject
  template: |-
    <!-- BEGIN_TF_DOCS -->
    {{ .Content }}
    <!-- END_TF_DOCS -->    

sort:
  enabled: false
  by: name

settings:
  anchor: true
  color: true
  default: true
  description: false
  escape: true
  hide-empty: false
  html: true
  indent: 2
  lockfile: true
  read-comments: true
  required: true
  sensitive: false
  type: true

A practical example

If you are using terraform-docs you may actually want to ensure, that the docs are created before you commit changes to git. This can be achieved by githooks

  1. I define a path where my script like

    git config core.hooksPath ~/.config/git/hooks
  2. Create the folder

    mkdir -p ~/.config/git/hooks
  3. In that folder I create a file - in this case for the pre-commit-hook- named pre-commit

  4. I make it executable and add the following code

    #!/usr/bin/env bash
    set -eo pipefail
    set -o errexit
    set -o errtrace
    shopt -s inherit_errexit
    
    if [[ -f "./.git/hooks/pre-commit" ]]; then
        sh ./.git/hooks/pre-commit
    fi
    
    # @description: generates the tf-doc incl. submodules if applicable
    function tfdocu_do() {
        # terraform-docs will locate any available configuration file without needing to explicitly pass the --config flag.
        if [ -z "$TF_DOCS_CONF" ]; then
            if [ -f "./.terraform-docs.yml" ]; then
                TF_DOCS_CONF="./.terraform-docs.yml"
            else
                TF_DOCS_CONF="$HOME/.tfdocs.d/.terraform-docs.yml"
            fi
        fi
        if terraform-docs -c $TF_DOCS_CONF $1; then
            git add "$1/README.md"
        fi
    }
    
    # @description: iterates over all modules and triggers creation of tf docu
    function tfdocu() {
        for dir in $(find . -maxdepth 1 -type d -not -path '*/.*' -not -path '.'); do
            echo "[Info] Running terraform-docs for '$dir'"
            tfdocu_do $dir
        done
    
        if [[ -n "$(ls -A *.tf 2>/dev/null)" ]]; then
            dir="."
            echo "[Info] Running terraform-docs for '$dir'"
            tfdocu_do $dir
        fi
    }
    
    ## we want terraform stuff only to be run for projects using terraform
    if [[ -n "$(ls -A *.tf 2>/dev/null)" || -n "$(find .  -maxdepth 2 -not -path '*/.*' -not -path '.' -type f -name '*.tf')" ]]; then
        tfdocu
        # "skeletons" will not be checked
        if [[ "$(pwd)" == *"skeleton"* ]]; then
            echo "[Info] This looks like a 'skeleton', so please manually run `terraform fmt`"
        else
            terraform fmt -recursive
        fi
    fi

So now when you do a git commit in a terraform related repo, it will create the docu automatically:

$ git commit -m"docu: use new template"
./module1
module1\README.md updated successfully
./module2
module2\README.md updated successfully
...
[feature/test-pre-commit-hook 5485ebd] docu: use new template
 1 files changed, 1 insertions(+), 1 deletions(-)

What’s next?

Rather than copying code around, setup a repo for your team where you store all shared git hooks. Mine can be found at here.

git clone [email protected]/papanito/git-hooks ~/.config/git/hooks