Terraform CI #2273
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: "Terraform CI" | |
| on: | |
| pull_request: | |
| merge_group: | |
| types: [checks_requested] | |
| push: | |
| branches: [main] | |
| env: | |
| GO_VERSION: "1.21" | |
| jobs: | |
| setup: | |
| name: "Setup and build matrix" | |
| runs-on: ubuntu-24.04-arm | |
| outputs: | |
| validation-matrix: ${{ steps.build-matrix.outputs.validation-matrix }} | |
| has-terraform-changes: ${{ steps.check-changes.outputs.has-changes }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| # Grab the last 75 commits. Later on we need to a git diff, and it's | |
| # very likely that two commits involved will be inside the last 75 | |
| fetch-depth: 75 | |
| - name: Initialise mise | |
| uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151 # v4.0.1 | |
| with: | |
| github_token: ${{ secrets.MISE_PAT }} # As this is a private repo, we can get API rate limit issues. Use a PAT to avoid this. (The token needs no special permissions.) | |
| install_args: go:github.com/hashicorp/terraform-config-inspect | |
| - name: Check for Terraform changes | |
| id: check-changes | |
| env: | |
| GIT_FROM: ${{github.event.pull_request.base.sha}} | |
| GIT_TO: ${{github.event.pull_request.head.sha}} | |
| run: | | |
| set -e -u -o pipefail | |
| if [[ $(git diff "${GIT_FROM}"..."${GIT_TO}" --name-only -- "*.tf" "*.tf.json" | wc -l) -eq 0 ]]; then | |
| echo "No Terraform files have changed." | |
| echo "has-changes=false" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "Terraform files have changed." | |
| echo "has-changes=true" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Build validation matrix | |
| id: build-matrix | |
| if: steps.check-changes.outputs.has-changes == 'true' | |
| env: | |
| GIT_FROM: ${{github.event.pull_request.base.sha}} | |
| GIT_TO: ${{github.event.pull_request.head.sha}} | |
| run: | | |
| set -e -u -o pipefail | |
| # Get modified directories | |
| git diff "${GIT_FROM}"..."${GIT_TO}" --name-only -- "*.tf" "*.tf.json" \ | |
| | xargs dirname \ | |
| | sort \ | |
| | uniq \ | |
| > modified-tf-dirs.txt | |
| # Find all roots | |
| all_roots=$(find infra/deployments -type d \ | |
| -mindepth 1 -maxdepth 2 \ | |
| -not -path "*/tfvars" \ | |
| -not -path "infra/deployments/forms" \ | |
| -not -path "infra/deployments/deploy" \ | |
| -not -path "infra/deployments/integration" | |
| ) | |
| # Build dependency graph and determine which roots need validation | |
| validation_dirs=() | |
| for start in ${all_roots}; do | |
| echo "Inspecting ${start}" | |
| stack=("${start}") | |
| visited=() | |
| while [[ "${#stack[@]}" -gt 0 ]]; do | |
| path="${stack[0]}" | |
| stack=("${stack[@]:1}") # Shift the front element off | |
| # shellcheck disable=SC2199 | |
| # shellcheck disable=SC2076 | |
| if [[ "${visited[@]}" =~ "${path}" ]]; then | |
| # Skip because it's already been visited | |
| continue | |
| fi | |
| visited+=("${path}") | |
| declare -a new_paths | |
| readarray -t new_paths < <(terraform-config-inspect --json "${path}" \ | |
| | jq -r '.module_calls | to_entries | .[] | .value.source' \ | |
| | sort \ | |
| | uniq \ | |
| | xargs -I{} readlink -f "${path}/{}" \ | |
| | xargs -I{} realpath --relative-to "$(pwd)" "{}" \ | |
| | sort | |
| ) | |
| if [[ "${#new_paths[@]}" -gt 0 ]]; then | |
| stack+=( "${stack[@]}" "${new_paths[@]}" ) | |
| fi | |
| done | |
| printf '%s\n' "${visited[@]}" | sort | uniq > "${start}/directories.txt" | |
| matching_lines="$(comm -1 -2 modified-tf-dirs.txt "${start}/directories.txt")" | |
| if [[ -n "$matching_lines" ]]; then | |
| echo "✓ ${start} requires validation" | |
| validation_dirs+=("${start}") | |
| else | |
| echo "✗ ${start} does not require validation" | |
| fi | |
| done | |
| # Convert to JSON array for matrix | |
| if [[ "${#validation_dirs[@]}" -gt 0 ]]; then | |
| validation_matrix=$(printf '%s\n' "${validation_dirs[@]}" | jq -Rnc '[inputs]') | |
| echo "validation-matrix=${validation_matrix}" >> "$GITHUB_OUTPUT" | |
| echo "Matrix: ${validation_matrix}" | |
| else | |
| echo "validation-matrix=[]" >> "$GITHUB_OUTPUT" | |
| echo "No directories require validation" | |
| fi | |
| terraform-fmt: | |
| name: "Terraform format check" | |
| needs: setup | |
| if: needs.setup.outputs.has-terraform-changes == 'true' | |
| runs-on: ubuntu-24.04-arm | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Initialise mise | |
| uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151 # v4.0.1 | |
| with: | |
| github_token: ${{ secrets.MISE_PAT }} | |
| install_args: terraform | |
| - name: Check Terraform style | |
| run: | | |
| terraform fmt -write=false -diff=true -list=true -recursive -check | |
| terraform-validate: | |
| name: "Terraform validate" | |
| runs-on: ubuntu-24.04-arm | |
| needs: setup | |
| if: needs.setup.outputs.has-terraform-changes == 'true' && needs.setup.outputs.validation-matrix != '[]' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| directory: ${{ fromJSON(needs.setup.outputs.validation-matrix) }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Initialise mise | |
| uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151 # v4.0.1 | |
| with: | |
| github_token: ${{ secrets.MISE_PAT }} | |
| install_args: terraform | |
| - name: Validate ${{ matrix.directory }} | |
| env: | |
| TF_PLUGIN_CACHE_DIR: "${{ runner.temp }}/terraform_cache" | |
| run: | | |
| set -e -u -o pipefail | |
| mkdir -p "$TF_PLUGIN_CACHE_DIR" | |
| echo "Validating ${{ matrix.directory }}" | |
| terraform -chdir="${{ matrix.directory }}" init -backend=false | |
| terraform -chdir="${{ matrix.directory }}" validate | |
| tflint: | |
| name: "tflint" | |
| needs: setup | |
| if: needs.setup.outputs.has-terraform-changes == 'true' | |
| runs-on: ubuntu-24.04-arm | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Initialise mise | |
| uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151 # v4.0.1 | |
| with: | |
| github_token: ${{ secrets.MISE_PAT }} | |
| install_args: tflint | |
| - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 | |
| name: Cache tflint plugin dir | |
| with: | |
| path: ~/.tflint.d/plugins | |
| key: tflint-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('.tflint.hcl') }} | |
| restore-keys: | | |
| tflint-${{ runner.arch }}-${{ runner.os }}- | |
| - name: Init tflint | |
| run: tflint --init | |
| env: | |
| # https://github.com/terraform-linters/tflint/blob/master/docs/user-guide/plugins.md#avoiding-rate-limiting | |
| GITHUB_TOKEN: ${{ github.token }} | |
| - name: Run tflint | |
| run: | | |
| make tflint | |
| checkov: | |
| name: "Checkov" | |
| needs: setup | |
| if: needs.setup.outputs.has-terraform-changes == 'true' | |
| runs-on: ubuntu-24.04-arm | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Initialise mise | |
| uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151 # v4.0.1 | |
| with: | |
| github_token: ${{ secrets.MISE_PAT }} | |
| install_args: checkov | |
| - name: Run Checkov against Terraform | |
| run: | | |
| checkov -d infra/ --external-checks-dir infra/checkov/ --framework terraform --quiet --skip-download | |
| report: | |
| # Required, for 'required PR status checks' on GitHub. | |
| name: "Run terraform tests" | |
| runs-on: ubuntu-24.04-arm | |
| needs: [terraform-fmt, terraform-validate, tflint, checkov] | |
| if: ${{ always() && !failure() && !cancelled() }} | |
| steps: | |
| - name: No checks failed | |
| run: echo "No terraform issues found." |