├── .gitignore ├── .github ├── dependabot.yml ├── labels.yml ├── workflows │ ├── release-drafter.yml │ ├── repository.yml │ ├── lint.yml │ ├── action_pull_request.yml │ ├── action_schedule.yml │ ├── action_branch.yml │ └── params.yml └── release-drafter.yml ├── .yamllint ├── LICENSE ├── tests ├── fail │ └── terragrunt.hcl └── ok │ └── terragrunt.hcl ├── Dockerfiles ├── Dockerfile └── data │ ├── fmt.sh │ └── terragrunt-fmt.sh ├── Makefile └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | Makefile.docker 2 | Makefile.lint 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | updates: 4 | # Maintain dependencies for GitHub Actions 5 | - package-ecosystem: "github-actions" 6 | directory: "/" 7 | schedule: 8 | interval: "daily" 9 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | --- 2 | extends: default 3 | 4 | ignore: | 5 | .yamllint 6 | 7 | 8 | rules: 9 | truthy: 10 | allowed-values: ['true', 'false'] 11 | check-keys: False 12 | level: error 13 | line-length: disable 14 | -------------------------------------------------------------------------------- /.github/labels.yml: -------------------------------------------------------------------------------- 1 | # The labels in this file are automatically synced with the repository 2 | # using the micnncim/action-label-syncer action. 3 | --- 4 | - name: C-dependency 5 | color: 1abc9c 6 | description: "Category: Dependency" 7 | - name: PR-block 8 | color: 3498db 9 | description: "Pull Request: Do not merge" 10 | - name: PR-merge 11 | color: 3498db 12 | description: "Pull Request: Merge when ready" 13 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Release Drafter 3 | 4 | on: 5 | push: 6 | # branches to consider in the event; optional, defaults to all 7 | branches: 8 | - master 9 | 10 | jobs: 11 | update_release_draft: 12 | runs-on: ubuntu-latest 13 | steps: 14 | # Drafts your next Release notes as Pull Requests are merged into "master" 15 | - uses: release-drafter/release-drafter@v5 16 | with: 17 | publish: true 18 | env: 19 | GITHUB_TOKEN: ${{ secrets.RELEASE_DRAFTER_TOKEN }} 20 | -------------------------------------------------------------------------------- /.github/workflows/repository.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Repository 3 | 4 | on: 5 | push: 6 | branches: 7 | - master 8 | paths: 9 | - .github/labels.yml 10 | 11 | jobs: 12 | labels: 13 | name: Labels 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Checkout code 18 | uses: actions/checkout@v3 19 | 20 | - name: Sync labels 21 | uses: micnncim/action-label-syncer@v1 22 | env: 23 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 24 | with: 25 | manifest: .github/labels.yml 26 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name-template: '$RESOLVED_VERSION 🌈' 3 | tag-template: '$RESOLVED_VERSION' 4 | version-template: '$MAJOR.$MINOR' 5 | categories: 6 | - title: '🚀 Features' 7 | labels: 8 | - 'feature' 9 | - 'enhancement' 10 | - title: '🐛 Bug Fixes' 11 | labels: 12 | - 'fix' 13 | - 'bugfix' 14 | - 'bug' 15 | - title: '🧰 Maintenance' 16 | label: 'chore' 17 | change-template: '- $TITLE @$AUTHOR (#$NUMBER)' 18 | change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. 19 | version-resolver: 20 | major: 21 | labels: 22 | - 'major' 23 | minor: 24 | labels: 25 | - 'minor' 26 | patch: 27 | labels: 28 | - 'patch' 29 | default: minor 30 | template: | 31 | ## Changes 32 | 33 | $CHANGES 34 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # ------------------------------------------------------------------------------------------------- 4 | # Job Name 5 | # ------------------------------------------------------------------------------------------------- 6 | name: lint 7 | 8 | 9 | # ------------------------------------------------------------------------------------------------- 10 | # When to run 11 | # ------------------------------------------------------------------------------------------------- 12 | on: 13 | # Runs on Pull Requests 14 | pull_request: 15 | 16 | 17 | # ------------------------------------------------------------------------------------------------- 18 | # What to run 19 | # ------------------------------------------------------------------------------------------------- 20 | jobs: 21 | lint: 22 | uses: devilbox/github-actions/.github/workflows/lint-generic.yml@master 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 cytopia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/action_pull_request.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # ------------------------------------------------------------------------------------------------- 4 | # Job Name 5 | # ------------------------------------------------------------------------------------------------- 6 | name: build 7 | 8 | 9 | # ------------------------------------------------------------------------------------------------- 10 | # When to run 11 | # ------------------------------------------------------------------------------------------------- 12 | on: 13 | pull_request: 14 | 15 | 16 | jobs: 17 | 18 | # (1/2) Determine repository params 19 | params: 20 | uses: ./.github/workflows/params.yml 21 | # Only run for forks (contributor) 22 | if: github.event.pull_request.head.repo.fork 23 | 24 | # (2/2) Build 25 | docker: 26 | needs: [params] 27 | uses: devilbox/github-actions/.github/workflows/docker-name-version-flavour-arch.yml@master 28 | with: 29 | enabled: true 30 | can_deploy: false 31 | matrix: ${{ needs.params.outputs.matrix }} 32 | refs: ${{ needs.params.outputs.refs }} 33 | secrets: 34 | dockerhub_username: "" 35 | dockerhub_password: "" 36 | -------------------------------------------------------------------------------- /.github/workflows/action_schedule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # ------------------------------------------------------------------------------------------------- 4 | # Job Name 5 | # ------------------------------------------------------------------------------------------------- 6 | name: nightly 7 | 8 | 9 | # ------------------------------------------------------------------------------------------------- 10 | # When to run 11 | # ------------------------------------------------------------------------------------------------- 12 | on: 13 | # Runs daily 14 | schedule: 15 | - cron: '0 0 * * *' 16 | 17 | 18 | jobs: 19 | 20 | # (1/2) Determine repository params 21 | params: 22 | uses: ./.github/workflows/params.yml 23 | 24 | # (2/2) Build 25 | docker: 26 | needs: [params] 27 | uses: devilbox/github-actions/.github/workflows/docker-name-version-flavour-arch.yml@master 28 | with: 29 | enabled: true 30 | can_deploy: true 31 | matrix: ${{ needs.params.outputs.matrix }} 32 | refs: ${{ needs.params.outputs.refs }} 33 | secrets: 34 | dockerhub_username: ${{ secrets.DOCKERHUB_USERNAME }} 35 | dockerhub_password: ${{ secrets.DOCKERHUB_PASSWORD }} 36 | -------------------------------------------------------------------------------- /tests/fail/terragrunt.hcl: -------------------------------------------------------------------------------- 1 | terraform { 2 | extra_arguments "common_vars" { 3 | commands = [ 4 | "apply", 5 | "plan", 6 | "import", 7 | "push", 8 | "refresh", 9 | "destroy", 10 | ] 11 | 12 | required_var_files = [ 13 | "${get_terragrunt_dir()}/terraform.tfvars", 14 | ] 15 | 16 | optional_var_files = [ 17 | "${get_terragrunt_dir()}/${find_in_parent_folders("region.tfvars")}", 18 | ] 19 | } 20 | 21 | extra_arguments "disable_input" { 22 | commands = get_terraform_commands_that_need_vars() 23 | arguments = ["-input=true"] 24 | } 25 | 26 | after_hook "copy_common_main_providers" { 27 | commands = ["init-from-module"] 28 | execute = ["cp", "${get_parent_terragrunt_dir()}/../_provider_include/include_providers.tf", "${get_terragrunt_dir()}"] 29 | } 30 | } 31 | 32 | remote_state { 33 | backend = "s3" 34 | config = { 35 | encrypt = true 36 | bucket = "my-tg-bucket" 37 | key = "${path_relative_to_include()}/terraform.tfstate" 38 | region = "eu-central-1" 39 | dynamodb_table = "my-tf-lock" 40 | } 41 | } 42 | 43 | inputs = { 44 | env = "dev" 45 | allowed_account_ids = ["123456789"] 46 | } 47 | -------------------------------------------------------------------------------- /tests/ok/terragrunt.hcl: -------------------------------------------------------------------------------- 1 | terraform { 2 | extra_arguments "common_vars" { 3 | commands = [ 4 | "apply", 5 | "plan", 6 | "import", 7 | "push", 8 | "refresh", 9 | "destroy", 10 | ] 11 | 12 | required_var_files = [ 13 | "${get_terragrunt_dir()}/terraform.tfvars", 14 | ] 15 | 16 | optional_var_files = [ 17 | "${get_terragrunt_dir()}/${find_in_parent_folders("region.tfvars")}", 18 | ] 19 | } 20 | 21 | extra_arguments "disable_input" { 22 | commands = get_terraform_commands_that_need_vars() 23 | arguments = ["-input=true"] 24 | } 25 | 26 | after_hook "copy_common_main_providers" { 27 | commands = ["init-from-module"] 28 | execute = ["cp", "${get_parent_terragrunt_dir()}/../_provider_include/include_providers.tf", "${get_terragrunt_dir()}"] 29 | } 30 | } 31 | 32 | remote_state { 33 | backend = "s3" 34 | config = { 35 | encrypt = true 36 | bucket = "my-tg-bucket" 37 | key = "${path_relative_to_include()}/terraform.tfstate" 38 | region = "eu-central-1" 39 | dynamodb_table = "my-tf-lock" 40 | } 41 | } 42 | 43 | inputs = { 44 | env = "dev" 45 | allowed_account_ids = ["123456789"] 46 | } 47 | -------------------------------------------------------------------------------- /.github/workflows/action_branch.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # ------------------------------------------------------------------------------------------------- 4 | # Job Name 5 | # ------------------------------------------------------------------------------------------------- 6 | name: build 7 | 8 | 9 | # ------------------------------------------------------------------------------------------------- 10 | # When to run 11 | # ------------------------------------------------------------------------------------------------- 12 | on: 13 | push: 14 | paths: 15 | - 'Makefile' 16 | - 'Dockerfiles/**' 17 | - 'tests/**' 18 | - '.github/workflows/action*.yml' 19 | - '.github/workflows/params.yml' 20 | 21 | jobs: 22 | 23 | # (1/2) Determine repository params 24 | params: 25 | uses: ./.github/workflows/params.yml 26 | 27 | # (2/2) Build 28 | docker: 29 | needs: [params] 30 | uses: devilbox/github-actions/.github/workflows/docker-name-version-flavour-arch.yml@master 31 | with: 32 | enabled: true 33 | can_deploy: ${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/') || startsWith(github.ref, 'refs/heads/release-') }} 34 | matrix: ${{ needs.params.outputs.matrix }} 35 | refs: ${{ needs.params.outputs.refs }} 36 | secrets: 37 | dockerhub_username: ${{ secrets.DOCKERHUB_USERNAME }} 38 | dockerhub_password: ${{ secrets.DOCKERHUB_PASSWORD }} 39 | -------------------------------------------------------------------------------- /Dockerfiles/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.16 as builder 2 | 3 | # Install build dependencies 4 | RUN set -eux \ 5 | && apk add --no-cache \ 6 | ca-certificates \ 7 | coreutils \ 8 | curl \ 9 | unzip 10 | 11 | # Get Terraform 12 | ARG VERSION=latest 13 | RUN set -eux \ 14 | && if [ "${VERSION}" = "latest" ]; then \ 15 | VERSION="$( curl -sS https://releases.hashicorp.com/terraform/ \ 16 | | tac | tac \ 17 | | grep -Eo '/[.0-9]+/' \ 18 | | grep -Eo '[.0-9]+' \ 19 | | sort -V \ 20 | | tail -1 )"; \ 21 | else \ 22 | VERSION="$( curl -sS https://releases.hashicorp.com/terraform/ \ 23 | | tac | tac \ 24 | | grep -Eo "/${VERSION}\.[.0-9]+/" \ 25 | | grep -Eo '[.0-9]+' \ 26 | | sort -V \ 27 | | tail -1 )"; \ 28 | fi \ 29 | && curl -sS -L -O \ 30 | https://releases.hashicorp.com/terraform/${VERSION}/terraform_${VERSION}_linux_amd64.zip \ 31 | && unzip terraform_${VERSION}_linux_amd64.zip \ 32 | && mv terraform /usr/bin/terraform \ 33 | && chmod +x /usr/bin/terraform \ 34 | \ 35 | && terraform --version 36 | 37 | # Use a clean tiny image to store artifacts in 38 | FROM alpine:3.16 as production 39 | LABEL \ 40 | maintainer="cytopia " \ 41 | repo="https://github.com/cytopia/docker-terragrunt-fmt" 42 | 43 | COPY --from=builder /usr/bin/terraform /usr/bin/terraform 44 | COPY data/terragrunt-fmt.sh /terragrunt-fmt.sh 45 | COPY data/fmt.sh /fmt.sh 46 | 47 | WORKDIR /data 48 | ENTRYPOINT ["/terragrunt-fmt.sh"] 49 | -------------------------------------------------------------------------------- /Dockerfiles/data/fmt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Be strict 4 | set -e 5 | set -u 6 | 7 | ### 8 | ### Inputs 9 | ### 10 | _list="${1}" 11 | _write="${2}" 12 | _diff="${3}" 13 | _check="${4}" 14 | _file="${5}" 15 | # shellcheck disable=SC2155 16 | _temp="/tmp/$(basename "${_file}").tf" 17 | _ret=0 18 | 19 | 20 | ### 21 | ### Build command (only append if default values are overwritten) 22 | ### 23 | _cmd="terraform fmt" 24 | if [ "${_list}" = "0" ]; then 25 | _cmd="${_cmd} -list=false" 26 | else 27 | _cmd="${_cmd} -list=true" 28 | fi 29 | if [ "${_write}" = "1" ]; then 30 | _cmd="${_cmd} -write=true" 31 | else 32 | _cmd="${_cmd} -write=false" 33 | fi 34 | if [ "${_diff}" = "1" ]; then 35 | _cmd="${_cmd} -diff" 36 | fi 37 | if [ "${_check}" = "1" ]; then 38 | _cmd="${_cmd} -check" 39 | fi 40 | 41 | ### 42 | ### Output and execute command 43 | ### 44 | echo "${_cmd} ${_file}" 45 | cp -f "${_file}" "${_temp}" 46 | if ! eval "${_cmd} ${_temp}"; then 47 | _ret=1 48 | fi 49 | 50 | ### 51 | ### If -write was specified, copy file back 52 | ### 53 | if [ "${_write}" = "1" ]; then 54 | # Get owner and permissions of current file 55 | _UID="$(stat -c %u "${_file}")" 56 | _GID="$(stat -c %g "${_file}")" 57 | _PERM="$(stat -c %a "${_file}")" 58 | 59 | # Adjust permissions of temporary file 60 | chown "${_UID}:${_GID}" "${_temp}" 61 | chmod "${_PERM}" "${_temp}" 62 | 63 | # Overwrite existing file 64 | mv -f "${_temp}" "${_file}" 65 | fi 66 | 67 | ### 68 | ### Exit 69 | ### 70 | exit "${_ret}" 71 | -------------------------------------------------------------------------------- /.github/workflows/params.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # ------------------------------------------------------------------------------------------------- 4 | # Job Name 5 | # ------------------------------------------------------------------------------------------------- 6 | name: params 7 | 8 | 9 | # ------------------------------------------------------------------------------------------------- 10 | # Custom Variables 11 | # ------------------------------------------------------------------------------------------------- 12 | env: 13 | MATRIX: >- 14 | [ 15 | { 16 | "NAME": "tf-fmt", 17 | "VERSION": [ 18 | "latest", 19 | "1.3", 20 | "1.2", 21 | "1.1", 22 | "1.0", 23 | "0.15", 24 | "0.14", 25 | "0.13", 26 | "0.12" 27 | ], 28 | "FLAVOUR": ["latest"], 29 | "ARCH": ["linux/amd64", "linux/386", "linux/arm64", "linux/arm/v7", "linux/arm/v6", "linux/ppc64le", "linux/s390x"] 30 | } 31 | ] 32 | 33 | 34 | # ------------------------------------------------------------------------------------------------- 35 | # When to run 36 | # ------------------------------------------------------------------------------------------------- 37 | on: 38 | workflow_call: 39 | outputs: 40 | matrix: 41 | description: "The determined version matrix" 42 | value: ${{ jobs.params.outputs.matrix }} 43 | refs: 44 | description: "The determined git ref matrix (only during scheduled run)" 45 | value: ${{ jobs.params.outputs.refs }} 46 | 47 | jobs: 48 | params: 49 | runs-on: ubuntu-latest 50 | 51 | outputs: 52 | matrix: ${{ steps.set-matrix.outputs.matrix }} 53 | refs: ${{ steps.set-refs.outputs.matrix }} 54 | 55 | steps: 56 | - name: "[Set-Output] Matrix" 57 | id: set-matrix 58 | run: | 59 | echo "matrix=$( echo '${{ env.MATRIX }}' | jq -M -c )" >> $GITHUB_OUTPUT 60 | 61 | - name: "[Set-Output] Matrix 'Refs' (master branch and latest tag)" 62 | id: set-refs 63 | uses: cytopia/git-ref-matrix-action@v0.1.13 64 | with: 65 | repository_default_branch: master 66 | branches: master 67 | num_latest_tags: 0 68 | if: github.event_name == 'schedule' 69 | 70 | - name: "[DEBUG] Show settings'" 71 | run: | 72 | echo 'Matrix' 73 | echo '--------------------' 74 | echo '${{ steps.set-matrix.outputs.matrix }}' 75 | echo 76 | 77 | echo 'Matrix: Refs' 78 | echo '--------------------' 79 | echo '${{ steps.set-matrix-refs.outputs.matrix }}' 80 | echo 81 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ifneq (,) 2 | .error This Makefile requires GNU Make. 3 | endif 4 | 5 | # Ensure additional Makefiles are present 6 | MAKEFILES = Makefile.docker Makefile.lint 7 | $(MAKEFILES): URL=https://raw.githubusercontent.com/devilbox/makefiles/master/$(@) 8 | $(MAKEFILES): 9 | @if ! (curl --fail -sS -o $(@) $(URL) || wget -O $(@) $(URL)); then \ 10 | echo "Error, curl or wget required."; \ 11 | echo "Exiting."; \ 12 | false; \ 13 | fi 14 | include $(MAKEFILES) 15 | 16 | # Set default Target 17 | .DEFAULT_GOAL := help 18 | 19 | 20 | # ------------------------------------------------------------------------------------------------- 21 | # Default configuration 22 | # ------------------------------------------------------------------------------------------------- 23 | # Own vars 24 | TAG = latest 25 | 26 | # Makefile.docker overwrites 27 | NAME = tg-fmt 28 | VERSION = latest 29 | IMAGE = cytopia/terragrunt-fmt 30 | FLAVOUR = latest 31 | DIR = Dockerfiles 32 | 33 | FILE = Dockerfile 34 | 35 | # Building from master branch: Tag == 'latest' 36 | ifeq ($(strip $(TAG)),latest) 37 | ifeq ($(strip $(VERSION)),latest) 38 | DOCKER_TAG = latest 39 | else 40 | DOCKER_TAG = $(VERSION) 41 | endif 42 | # Building from any other branch or tag: Tag == '' 43 | else 44 | ifeq ($(strip $(VERSION)),latest) 45 | DOCKER_TAG = latest-$(TAG) 46 | else 47 | DOCKER_TAG = $(VERSION)-$(TAG) 48 | endif 49 | endif 50 | 51 | # Makefile.lint overwrites 52 | FL_IGNORES = .git/,.github/ 53 | SC_IGNORES = .git/,.github/ 54 | JL_IGNORES = .git/,.github/ 55 | 56 | 57 | # ------------------------------------------------------------------------------------------------- 58 | # Default Target 59 | # ------------------------------------------------------------------------------------------------- 60 | .PHONY: help 61 | help: 62 | @echo "lint Lint project files and repository" 63 | @echo 64 | @echo "build [ARCH=...] [TAG=...] Build Docker image" 65 | @echo "rebuild [ARCH=...] [TAG=...] Build Docker image without cache" 66 | @echo "push [ARCH=...] [TAG=...] Push Docker image to Docker hub" 67 | @echo 68 | @echo "manifest-create [ARCHES=...] [TAG=...] Create multi-arch manifest" 69 | @echo "manifest-push [TAG=...] Push multi-arch manifest" 70 | @echo 71 | @echo "test [ARCH=...] Test built Docker image" 72 | @echo 73 | 74 | 75 | # ------------------------------------------------------------------------------------------------- 76 | # Docker Targets 77 | # ------------------------------------------------------------------------------------------------- 78 | .PHONY: build 79 | build: ARGS+=--build-arg VERSION=$(VERSION) 80 | build: docker-arch-build 81 | 82 | .PHONY: rebuild 83 | rebuild: ARGS+=--build-arg VERSION=$(VERSION) 84 | rebuild: docker-arch-rebuild 85 | 86 | .PHONY: push 87 | push: docker-arch-push 88 | 89 | 90 | # ------------------------------------------------------------------------------------------------- 91 | # Manifest Targets 92 | # ------------------------------------------------------------------------------------------------- 93 | .PHONY: manifest-create 94 | manifest-create: docker-manifest-create 95 | 96 | .PHONY: manifest-push 97 | manifest-push: docker-manifest-push 98 | 99 | 100 | # ------------------------------------------------------------------------------------------------- 101 | # Test Targets 102 | # ------------------------------------------------------------------------------------------------- 103 | .PHONY: test 104 | test: _test-tf-version 105 | test: _test-fmt-ok 106 | test: _test-fmt-none 107 | test: _test-fmt-fail 108 | 109 | .PHONY: _test-tf-version 110 | _test-tf-version: 111 | @echo "------------------------------------------------------------" 112 | @echo "- Testing correct Terraform version" 113 | @echo "------------------------------------------------------------" 114 | @if [ "$(VERSION)" = "latest" ]; then \ 115 | echo "Fetching latest version from HashiCorp release page"; \ 116 | LATEST="$$( \ 117 | curl -L -sS https://releases.hashicorp.com/terraform/ \ 118 | | tac | tac \ 119 | | grep -Eo '/[.0-9]+/' \ 120 | | grep -Eo '[.0-9]+' \ 121 | | sort -V \ 122 | | tail -1 \ 123 | )"; \ 124 | echo "Testing for latest: $${LATEST}"; \ 125 | if ! docker run --rm --platform $(ARCH) $(IMAGE):$(DOCKER_TAG) --version | grep -E "^Terraform[[:space:]]*v?$${LATEST}$$"; then \ 126 | docker run --rm --platform $(ARCH) $(IMAGE):$(DOCKER_TAG) --version; \ 127 | echo "Failed"; \ 128 | exit 1; \ 129 | fi; \ 130 | else \ 131 | echo "Testing for tag: $(VERSION)"; \ 132 | if ! docker run --rm --platform $(ARCH) $(IMAGE):$(DOCKER_TAG) --version | grep -E "^Terraform[[:space:]]*v?$(VERSION)\.[.0-9]+$$"; then \ 133 | docker run --rm --platform $(ARCH) $(IMAGE):$(DOCKER_TAG) --version; \ 134 | echo "Failed"; \ 135 | exit 1; \ 136 | fi; \ 137 | fi; \ 138 | echo "Success"; \ 139 | 140 | .PHONY: _test-fmt-ok 141 | _test-fmt-ok: 142 | @echo "------------------------------------------------------------" 143 | @echo "- Testing terragrunt-fmt (OK) [recursive]" 144 | @echo "------------------------------------------------------------" 145 | @if ! docker run --rm --platform $(ARCH) -v $(CURRENT_DIR)/tests/ok:/data $(IMAGE):$(DOCKER_TAG) -write=false -list=true -check -diff -recursive; then \ 146 | echo "Failed"; \ 147 | exit 1; \ 148 | fi; \ 149 | echo "Success"; 150 | @echo "------------------------------------------------------------" 151 | @echo "- Testing terragrunt-fmt (OK) [dir]" 152 | @echo "------------------------------------------------------------" 153 | @if ! docker run --rm --platform $(ARCH) -v $(CURRENT_DIR)/tests/ok:/data $(IMAGE):$(DOCKER_TAG) -write=false -list=true -check -diff; then \ 154 | echo "Failed"; \ 155 | exit 1; \ 156 | fi; \ 157 | echo "Success"; 158 | @echo "------------------------------------------------------------" 159 | @echo "- Testing terragrunt-fmt (OK) [file]" 160 | @echo "------------------------------------------------------------" 161 | @if ! docker run --rm --platform $(ARCH) -v $(CURRENT_DIR)/tests/ok:/data $(IMAGE):$(DOCKER_TAG) -write=false -list=true -check -diff terragrunt.hcl; then \ 162 | echo "Failed"; \ 163 | exit 1; \ 164 | fi; \ 165 | echo "Success"; 166 | 167 | .PHONY: _test-fmt-none 168 | _test-fmt-none: 169 | @echo "------------------------------------------------------------" 170 | @echo "- Testing terragrunt-fmt (NONE) [recursive]" 171 | @echo "------------------------------------------------------------" 172 | @if ! docker run --rm --platform $(ARCH) -v $(CURRENT_DIR)/Dockerfiles/data:/data $(IMAGE):$(DOCKER_TAG) -write=false -list=true -check -diff -recursive; then \ 173 | echo "Failed"; \ 174 | exit 1; \ 175 | fi; \ 176 | echo "Success"; 177 | @echo "------------------------------------------------------------" 178 | @echo "- Testing terragrunt-fmt (NONE) [dir]" 179 | @echo "------------------------------------------------------------" 180 | @if ! docker run --rm --platform $(ARCH) -v $(CURRENT_DIR)/Dockerfiles/data:/data $(IMAGE):$(DOCKER_TAG) -write=false -list=true -check -diff; then \ 181 | echo "Failed"; \ 182 | exit 1; \ 183 | fi; \ 184 | echo "Success"; 185 | 186 | .PHONY: _test-fmt-fail 187 | _test-fmt-fail: 188 | @echo "------------------------------------------------------------" 189 | @echo "- Testing terragrunt-fmt (FAIL) [recursive]" 190 | @echo "------------------------------------------------------------" 191 | @if docker run --rm --platform $(ARCH) -v $(CURRENT_DIR)/tests/fail:/data $(IMAGE):$(DOCKER_TAG) -write=false -list=true -check -diff -recursive; then \ 192 | echo "Failed"; \ 193 | exit 1; \ 194 | fi; \ 195 | echo "Success"; 196 | @echo "------------------------------------------------------------" 197 | @echo "- Testing terragrunt-fmt (FAIL) [dir]" 198 | @echo "------------------------------------------------------------" 199 | @if docker run --rm --platform $(ARCH) -v $(CURRENT_DIR)/tests/fail:/data $(IMAGE):$(DOCKER_TAG) -write=false -list=true -check -diff; then \ 200 | echo "Failed"; \ 201 | exit 1; \ 202 | fi; \ 203 | echo "Success"; 204 | @echo "------------------------------------------------------------" 205 | @echo "- Testing terragrunt-fmt (FAIL) [file]" 206 | @echo "------------------------------------------------------------" 207 | @if docker run --rm --platform $(ARCH) -v $(CURRENT_DIR)/tests/fail:/data $(IMAGE):$(DOCKER_TAG) -write=false -list=true -check -diff terragrunt.hcl; then \ 208 | echo "Failed"; \ 209 | exit 1; \ 210 | fi; \ 211 | echo "Success"; 212 | -------------------------------------------------------------------------------- /Dockerfiles/data/terragrunt-fmt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Be strict 4 | set -e 5 | set -u 6 | 7 | 8 | # -------------------------------------------------------------------------------- 9 | # VARIABLES 10 | # -------------------------------------------------------------------------------- 11 | ARG_LIST=1 12 | ARG_WRITE=0 13 | ARG_DIFF=0 14 | ARG_CHECK=0 15 | ARG_RECURSIVE=0 16 | ARG_IGNORE= 17 | ARG_PATH= 18 | 19 | 20 | # -------------------------------------------------------------------------------- 21 | # FUNCTIONS 22 | # -------------------------------------------------------------------------------- 23 | 24 | ### 25 | ### Show Usage 26 | ### 27 | print_usage() { 28 | echo "Usage: cytopia/terragrunt-fmt [options] [DIR]" 29 | echo " cytopia/terragrunt-fmt --help" 30 | echo " cytopia/terragrunt-fmt --version" 31 | echo 32 | echo " Rewrites all Terragrunt configuration files to a canonical format. All" 33 | echo " hcl configuration files (.hcl) are updated." 34 | echo 35 | echo " If DIR is not specified then the current working directory will be used." 36 | echo 37 | echo "Options:" 38 | echo 39 | echo " -list=true List files whose formatting differs" 40 | echo 41 | echo " -write=false Don't write to source files" 42 | echo " (always disabled if using -check)" 43 | echo 44 | echo " -diff Display diffs of formatting changes" 45 | echo 46 | echo " -check Check if the input is formatted. Exit status will be 0 if all" 47 | echo " input is properly formatted and non-zero otherwise." 48 | echo 49 | echo " -recursive Also process files in subdirectories. By default, only the" 50 | echo " given directory (or current directory) is processed." 51 | echo 52 | echo " -ignore=a,b Comma separated list of paths to ignore." 53 | echo " The wildcard character '*' is supported." 54 | } 55 | 56 | 57 | # -------------------------------------------------------------------------------- 58 | # ENTRYPOINT (EVALUATE ARGUMENTS) 59 | # -------------------------------------------------------------------------------- 60 | if [ "${#}" -gt "0" ]; then 61 | 62 | while [ "${#}" -gt "0" ]; do 63 | case "${1}" in 64 | # Show Help and exit 65 | --help) 66 | print_usage 67 | exit 0 68 | ;; 69 | # Show Version and exit 70 | --version) 71 | terraform --version 72 | exit 0 73 | ;; 74 | # List files 75 | -list | -list=*) 76 | if [ "${1}" = "-list" ]; then 77 | ARG_LIST=1 78 | else 79 | _ARG="${1##*=}" 80 | if [ "${_ARG}" = "true" ]; then 81 | ARG_LIST=1 82 | elif [ "${_ARG}" = "false" ]; then 83 | ARG_LIST=0 84 | else 85 | >&2 echo "Error, -list can only be set to 'true' or 'false', you had: '${_ARG}'" 86 | exit 1 87 | fi 88 | fi 89 | shift 90 | ;; 91 | # Write files 92 | -write | -write=*) 93 | if [ "${1}" = "-write" ]; then 94 | if [ "${ARG_CHECK}" = "1" ]; then 95 | >&2 echo "Error, -check and -write cannot be used together" 96 | exit 1 97 | fi 98 | ARG_WRITE=1 99 | else 100 | _ARG="${1##*=}" 101 | if [ "${_ARG}" = "true" ]; then 102 | if [ "${ARG_CHECK}" = "1" ]; then 103 | >&2 echo "Error, -check and -write=true cannot be used together" 104 | exit 1 105 | fi 106 | ARG_WRITE=1 107 | elif [ "${_ARG}" = "false" ]; then 108 | ARG_WRITE=0 109 | else 110 | >&2 echo "Error, -write can only be set to 'true' or 'false', you had: '${_ARG}'" 111 | exit 1 112 | fi 113 | fi 114 | shift 115 | ;; 116 | # Diff files 117 | -diff | -diff=*) 118 | if [ "${1}" = "-diff" ]; then 119 | ARG_DIFF=1 120 | else 121 | _ARG="${1##*=}" 122 | if [ "${_ARG}" = "true" ]; then 123 | ARG_DIFF=1 124 | elif [ "${_ARG}" = "false" ]; then 125 | ARG_DIFF=0 126 | else 127 | >&2 echo "Error, -diff can only be set to 'true' or 'false', you had: '${_ARG}'" 128 | exit 1 129 | fi 130 | fi 131 | shift 132 | ;; 133 | # Check files 134 | -check | -check=*) 135 | if [ "${1}" = "-check" ]; then 136 | if [ "${ARG_WRITE}" = "1" ]; then 137 | >&2 echo "Error, -check and -write=true cannot be used together" 138 | exit 1 139 | fi 140 | ARG_CHECK=1 141 | else 142 | _ARG="${1##*=}" 143 | if [ "${_ARG}" = "true" ]; then 144 | if [ "${ARG_WRITE}" = "1" ]; then 145 | >&2 echo "Error, -check=true and -write=true cannot be used together" 146 | exit 1 147 | fi 148 | ARG_CHECK=1 149 | elif [ "${_ARG}" = "false" ]; then 150 | ARG_CHECK=0 151 | else 152 | >&2 echo "Error, -check can only be set to 'true' or 'false', you had: '${_ARG}'" 153 | exit 1 154 | fi 155 | fi 156 | shift 157 | ;; 158 | # Recursive apply 159 | -recursive | -recursive=*) 160 | if [ "${1}" = "-recursive" ]; then 161 | ARG_RECURSIVE=1 162 | else 163 | _ARG="${1##*=}" 164 | if [ "${_ARG}" = "true" ]; then 165 | ARG_RECURSIVE=1 166 | elif [ "${_ARG}" = "false" ]; then 167 | ARG_RECURSIVE=0 168 | else 169 | >&2 echo "Error, -recursive can only be set to 'true' or 'false', you had: '${_ARG}'" 170 | exit 1 171 | fi 172 | fi 173 | shift 174 | ;; 175 | # Paths to ignore 176 | -ignore | -ignore=*) 177 | if [ "${1}" = "-ignore" ]; then 178 | if [ "${#}" -lt "2" ]; then 179 | >&2 echo "Error, '${1}' requires an argument" 180 | exit 1 181 | fi 182 | shift 183 | ARG_IGNORE="${1}" 184 | else 185 | ARG_IGNORE="${1##*=}" 186 | fi 187 | shift 188 | ;; 189 | # Any other arguments are invalid 190 | -*) 191 | >&2 echo "Error, '${1}' is an unsupported argument" 192 | print_usage 193 | exit 1 194 | ;; 195 | # Anything else is the [DIR] 196 | *) 197 | # ---- Case 1/3: Its a file ---- 198 | if [ -f "${1}" ]; then 199 | # Argument check 200 | if [ "${1##*.}" != "hcl" ]; then 201 | >&2 echo "Error, only .hcl files are supported." 202 | exit 1 203 | fi 204 | if [ "${#}" -gt "1" ]; then 205 | >&2 echo "Error, you cannot specify arguments after the [DIR] position." 206 | print_usage 207 | exit 1 208 | fi 209 | if [ "${ARG_RECURSIVE}" = "1" ]; then 210 | >&2 echo "Error, -recursive makes no sense on a single file." 211 | exit 1 212 | fi 213 | 214 | # ---- Case 2/3: Its a directory ---- 215 | elif [ -d "${1}" ]; then 216 | # Argument check 217 | if [ "${#}" -gt "1" ]; then 218 | >&2 echo "Error, you cannot specify arguments after the [DIR] position." 219 | print_usage 220 | exit 1 221 | fi 222 | # ---- Case 3/3: File or directory does not exist ---- 223 | else 224 | >&2 echo "Error, file or directory does not exist: ${1}" 225 | exit 1 226 | fi 227 | ARG_PATH="${1}" 228 | shift 229 | ;; 230 | esac 231 | done 232 | fi 233 | 234 | 235 | # -------------------------------------------------------------------------------- 236 | # ENTRYPOINT (TERRAFORM FMT) 237 | # -------------------------------------------------------------------------------- 238 | 239 | # If no [DIR] was specified, use current directory 240 | if [ -z "${ARG_PATH}" ]; then 241 | ARG_PATH="." 242 | fi 243 | 244 | 245 | ### 246 | ### (1/3) Single file 247 | ### 248 | if [ -f "${ARG_PATH}" ]; then 249 | /fmt.sh "${ARG_LIST}" "${ARG_WRITE}" "${ARG_DIFF}" "${ARG_CHECK}" "${ARG_PATH}" 250 | exit "${?}" 251 | else 252 | ### 253 | ### (2/3) Recursive directory 254 | ### 255 | if [ "${ARG_RECURSIVE}" = "1" ]; then 256 | 257 | # evaluate ignore paths 258 | if [ -n "${ARG_IGNORE}" ]; then 259 | _EXCLUDE=" -not \( -path \"${ARG_PATH}/$( echo "${ARG_IGNORE}" | sed "s/,/*\" -o -path \"${ARG_PATH}\//g" )*\" \)" 260 | else 261 | _EXCLUDE="" 262 | fi 263 | 264 | # Store exit code 265 | echo "0" > "/tmp/exit.txt" 266 | 267 | find_cmd="find ${ARG_PATH}${_EXCLUDE} -name '*.hcl' -type f" 268 | echo "[INFO] Finding files: ${find_cmd}" 269 | eval "${find_cmd} -print0 | xargs -n1 -0 sh -c '\ 270 | if [ -f \"\${1}\" ]; then \ 271 | if ! /fmt.sh \"${ARG_LIST}\" \"${ARG_WRITE}\" \"${ARG_DIFF}\" \"${ARG_CHECK}\" \"\${1}\"; then \ 272 | echo 1 > /tmp/exit.txt; \ 273 | fi \ 274 | fi' --" 275 | 276 | # Read exit code and return it 277 | exit "$( cat /tmp/exit.txt )" 278 | 279 | ### 280 | ### (3/3) Current directory only 281 | ### 282 | else 283 | echo "[INFO] Finding files: for file in *.hcl; do" 284 | ret=0 285 | for file in *.hcl; do 286 | if [ -f "${file}" ]; then 287 | if ! /fmt.sh "${ARG_LIST}" "${ARG_WRITE}" "${ARG_DIFF}" "${ARG_CHECK}" "${file}"; then 288 | ret="1" 289 | fi 290 | fi 291 | done 292 | exit "${ret}" 293 | fi 294 | fi 295 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Docker image for `terragrunt-fmt` 2 | 3 | [![Tag](https://img.shields.io/github/tag/cytopia/docker-terragrunt-fmt.svg)](https://github.com/cytopia/docker-terragrunt-fmt/releases) 4 | [![](https://img.shields.io/badge/github-cytopia%2Fdocker--terragrunt--fmt-red.svg)](https://github.com/cytopia/docker-terragrunt-fmt "github.com/cytopia/docker-terragrunt-fmt") 5 | [![License](https://img.shields.io/badge/license-MIT-%233DA639.svg)](https://opensource.org/licenses/MIT) 6 | 7 | [![lint](https://github.com/cytopia/docker-terragrunt-fmt/workflows/lint/badge.svg)](https://github.com/cytopia/docker-terragrunt-fmt/actions?query=workflow%3Alint) 8 | [![build](https://github.com/cytopia/docker-terragrunt-fmt/workflows/build/badge.svg)](https://github.com/cytopia/docker-terragrunt-fmt/actions?query=workflow%3Abuild) 9 | [![nightly](https://github.com/cytopia/docker-terragrunt-fmt/workflows/nightly/badge.svg)](https://github.com/cytopia/docker-terragrunt-fmt/actions?query=workflow%3Anightly) 10 | 11 | 12 | > #### All [#awesome-ci](https://github.com/topics/awesome-ci) Docker images 13 | > 14 | > [ansible-lint][alint-git-lnk] **•** 15 | > [ansible][ansible-git-lnk] **•** 16 | > [awesome-ci][aci-git-lnk] **•** 17 | > [bandit][bandit-git-lnk] **•** 18 | > [black][black-git-lnk] **•** 19 | > [checkmake][cm-git-lnk] **•** 20 | > [eslint][elint-git-lnk] **•** 21 | > [file-lint][flint-git-lnk] **•** 22 | > [gofmt][gfmt-git-lnk] **•** 23 | > [goimports][gimp-git-lnk] **•** 24 | > [golint][glint-git-lnk] **•** 25 | > [jsonlint][jlint-git-lnk] **•** 26 | > [kubeval][kubeval-git-lnk] **•** 27 | > [linkcheck][linkcheck-git-lnk] **•** 28 | > [mypy][mypy-git-lnk] **•** 29 | > [php-cs-fixer][pcsf-git-lnk] **•** 30 | > [phpcbf][pcbf-git-lnk] **•** 31 | > [phpcs][pcs-git-lnk] **•** 32 | > [phplint][plint-git-lnk] **•** 33 | > [pycodestyle][pycs-git-lnk] **•** 34 | > [pydocstyle][pyds-git-lnk] **•** 35 | > [pylint][pylint-git-lnk] **•** 36 | > [terraform-docs][tfdocs-git-lnk] **•** 37 | > [terragrunt-fmt][tgfmt-git-lnk] **•** 38 | > [terragrunt][tg-git-lnk] **•** 39 | > [yamlfmt][yfmt-git-lnk] **•** 40 | > [yamllint][ylint-git-lnk] 41 | 42 | View **[Dockerfiles](https://github.com/cytopia/docker-terragrunt-fmt/blob/master/Dockerfiles/)** on GitHub. 43 | 44 | 45 | **Available Architectures:** `amd64`, `i386`, `arm64`, `arm/v7`, `arm/v6`, `ppc64le`, `s390x` 46 | 47 | Tiny Alpine-based multistage-build dockerized version of [Terraform](https://github.com/hashicorp/terraform)[1] with the ability to do `terraform fmt` on Terragrunt files (`.hcl`). 48 | This is achieved by creating a temporary file within the container with an `.tf` extension and then running `terraform fmt` on it. 49 | Additionally the wrapper has been extended with a **`-ignore` argument** to be able to ignore files and directory or wildcards. 50 | The image is built nightly against multiple stable versions and pushed to Dockerhub. 51 | 52 | [1] Official project: https://github.com/hashicorp/terraform 53 | 54 | ## :whale: Available Docker image versions 55 | 56 | [![](https://img.shields.io/docker/pulls/cytopia/terragrunt-fmt.svg)](https://hub.docker.com/r/cytopia/terragrunt-fmt) 57 | [![Docker](https://badgen.net/badge/icon/:latest?icon=docker&label=cytopia/terragrunt-fmt)](https://hub.docker.com/r/cytopia/terragrunt-fmt) 58 | 59 | #### Rolling releaess 60 | 61 | The following Docker image tags are rolling releases and are built and updated every night. 62 | 63 | [![nightly](https://github.com/cytopia/docker-terragrunt-fmt/workflows/nightly/badge.svg)](https://github.com/cytopia/docker-terragrunt-fmt/actions?query=workflow%3Anightly) 64 | 65 | | Docker Tag | Git Ref | terraform | Available Architectures | 66 | |----------------------|-----------|--------------|----------------------------------------------| 67 | | `latest` | master | latest | `amd64`, `i386`, `arm64`, `arm/v7`, `arm/v6`, `ppc64le`, `s390x` | 68 | | `1.3` | master | **`1.3.x`** | `amd64`, `i386`, `arm64`, `arm/v7`, `arm/v6`, `ppc64le`, `s390x` | 69 | | `1.2` | master | **`1.2.x`** | `amd64`, `i386`, `arm64`, `arm/v7`, `arm/v6`, `ppc64le`, `s390x` | 70 | | `1.1` | master | **`1.1.x`** | `amd64`, `i386`, `arm64`, `arm/v7`, `arm/v6`, `ppc64le`, `s390x` | 71 | | `1.0` | master | **`1.0.x`** | `amd64`, `i386`, `arm64`, `arm/v7`, `arm/v6`, `ppc64le`, `s390x` | 72 | | `0.15` | master | **`0.15.x`** | `amd64`, `i386`, `arm64`, `arm/v7`, `arm/v6`, `ppc64le`, `s390x` | 73 | | `0.14` | master | **`0.14.x`** | `amd64`, `i386`, `arm64`, `arm/v7`, `arm/v6`, `ppc64le`, `s390x` | 74 | | `0.13` | master | **`0.13.x`** | `amd64`, `i386`, `arm64`, `arm/v7`, `arm/v6`, `ppc64le`, `s390x` | 75 | | `0.12` | master | **`0.12.x`** | `amd64`, `i386`, `arm64`, `arm/v7`, `arm/v6`, `ppc64le`, `s390x` | 76 | 77 | #### Point in time releases 78 | 79 | The following Docker image tags are built once and can be used for reproducible builds. Its version never changes so you will have to update tags in your pipelines from time to time in order to stay up-to-date. 80 | 81 | [![build](https://github.com/cytopia/docker-terragrunt-fmt/workflows/build/badge.svg)](https://github.com/cytopia/docker-terragrunt-fmt/actions?query=workflow%3Abuild) 82 | 83 | | Docker Tag | Git Ref | terraform | Available Architectures | 84 | |----------------------|--------------|--------------|----------------------------------------------| 85 | | `latest-` | tag: `` | latest | `amd64`, `i386`, `arm64`, `arm/v7`, `arm/v6`, `ppc64le`, `s390x` | 86 | | `1.3-` | tag: `` | **`1.3.x`** | `amd64`, `i386`, `arm64`, `arm/v7`, `arm/v6`, `ppc64le`, `s390x` | 87 | | `1.2-` | tag: `` | **`1.2.x`** | `amd64`, `i386`, `arm64`, `arm/v7`, `arm/v6`, `ppc64le`, `s390x` | 88 | | `1.1-` | tag: `` | **`1.1.x`** | `amd64`, `i386`, `arm64`, `arm/v7`, `arm/v6`, `ppc64le`, `s390x` | 89 | | `1.0-` | tag: `` | **`1.0.x`** | `amd64`, `i386`, `arm64`, `arm/v7`, `arm/v6`, `ppc64le`, `s390x` | 90 | | `0.15-` | tag: `` | **`0.15.x`** | `amd64`, `i386`, `arm64`, `arm/v7`, `arm/v6`, `ppc64le`, `s390x` | 91 | | `0.14-` | tag: `` | **`0.14.x`** | `amd64`, `i386`, `arm64`, `arm/v7`, `arm/v6`, `ppc64le`, `s390x` | 92 | | `0.13-` | tag: `` | **`0.13.x`** | `amd64`, `i386`, `arm64`, `arm/v7`, `arm/v6`, `ppc64le`, `s390x` | 93 | | `0.12-` | tag: `` | **`0.12.x`** | `amd64`, `i386`, `arm64`, `arm/v7`, `arm/v6`, `ppc64le`, `s390x` | 94 | 95 | > Where `` refers to the chosen git tag from this repository. 96 | 97 | 98 | ## :open_file_folder: Docker mounts 99 | 100 | The working directory inside the Docker container is **`/data/`** and should be mounted locally to 101 | the root of your project. 102 | 103 | 104 | ## :computer: Usage 105 | 106 | Ignore all directories named `dev/` and `testing/` and everything inside. 107 | ```bash 108 | $ docker run --rm -v $(pwd):/data cytopia/terragrunt-fmt -recursive -ignore=*/dev/,*/testing/ 109 | 110 | [INFO] Finding files: find . -not \( -path "./*/dev/*" -o -path "./*/testing/*" \) -name '*.hcl' -type f 111 | terraform fmt -list=true -write=false ./prod/eu-central-1/microservice/terragrunt.hcl 112 | ../tmp/terragrunt.hcl.tf 113 | terraform fmt -list=true -write=false ./prod/eu-central-1/infra/terragrunt.hcl 114 | ../tmp/terragrunt.hcl.tf 115 | ``` 116 | 117 | 118 | ## Project and CI integration 119 | 120 | #### Makefile 121 | You can add the following Makefile to your project for easy linting anf fixing of Terragrunt `.hcl` files. 122 | ```make 123 | ifneq (,) 124 | .error This Makefile requires GNU Make. 125 | endif 126 | 127 | .PHONY: help lint fix _pull 128 | 129 | CURRENT_DIR = $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) 130 | 131 | # Adjust according to your needs 132 | IGNORE = */.terragrunt-cache/,*/.terraform/ 133 | FMT_VERSION = latest 134 | 135 | help: 136 | @echo "help Show this help" 137 | @echo "lint Exit > 0 if any files have wrong formatting" 138 | @echo "fix Fix all .hcl files" 139 | 140 | lint: _pull 141 | docker run --rm -v $(CURRENT_DIR):/data cytopia/terragrunt-fmt:$(FMT_VERSION) \ 142 | -check -diff -recursive -ignore='$(IGNORE)' 143 | 144 | fix: _pull 145 | docker run --rm -v $(CURRENT_DIR):/data cytopia/terragrunt-fmt:$(FMT_VERSION) \ 146 | -write -diff -recursive -ignore='$(IGNORE)' 147 | 148 | _pull: 149 | docker pull cytopia/terragrunt-fmt:$(FMT_VERSION) 150 | ``` 151 | 152 | #### Travis CI integration 153 | With the above Makefile in place, you can easily add a Travis CI rule to ensure the Terragrunt code 154 | uses correct coding style. 155 | 156 | ```yml 157 | --- 158 | sudo: required 159 | language: minimal 160 | services: 161 | - docker 162 | script: 163 | - make lint 164 | ``` 165 | 166 | 167 | ## :arrows_counterclockwise: Related [#awesome-ci](https://github.com/topics/awesome-ci) projects 168 | 169 | ### Docker images 170 | 171 | Save yourself from installing lot's of dependencies and pick a dockerized version of your favourite 172 | linter below for reproducible local or remote CI tests: 173 | 174 | | GitHub | DockerHub | Type | Description | 175 | |--------|-----------|------|-------------| 176 | | [awesome-ci][aci-git-lnk] | [![aci-hub-img]][aci-hub-lnk] | Basic | Tools for git, file and static source code analysis | 177 | | [file-lint][flint-git-lnk] | [![flint-hub-img]][flint-hub-lnk] | Basic | Baisc source code analysis | 178 | | [linkcheck][linkcheck-git-lnk] | [![linkcheck-hub-img]][flint-hub-lnk] | Basic | Search for URLs in files and validate their HTTP status code | 179 | | [ansible][ansible-git-lnk] | [![ansible-hub-img]][ansible-hub-lnk] | Ansible | Multiple versions and flavours of Ansible | 180 | | [ansible-lint][alint-git-lnk] | [![alint-hub-img]][alint-hub-lnk] | Ansible | Lint Ansible | 181 | | [gofmt][gfmt-git-lnk] | [![gfmt-hub-img]][gfmt-hub-lnk] | Go | Format Go source code **[1]** | 182 | | [goimports][gimp-git-lnk] | [![gimp-hub-img]][gimp-hub-lnk] | Go | Format Go source code **[1]** | 183 | | [golint][glint-git-lnk] | [![glint-hub-img]][glint-hub-lnk] | Go | Lint Go code | 184 | | [eslint][elint-git-lnk] | [![elint-hub-img]][elint-hub-lnk] | Javascript | Lint Javascript code | 185 | | [jsonlint][jlint-git-lnk] | [![jlint-hub-img]][jlint-hub-lnk] | JSON | Lint JSON files **[1]** | 186 | | [kubeval][kubeval-git-lnk] | [![kubeval-hub-img]][kubeval-hub-lnk] | K8s | Lint Kubernetes files | 187 | | [checkmake][cm-git-lnk] | [![cm-hub-img]][cm-hub-lnk] | Make | Lint Makefiles | 188 | | [phpcbf][pcbf-git-lnk] | [![pcbf-hub-img]][pcbf-hub-lnk] | PHP | PHP Code Beautifier and Fixer | 189 | | [phpcs][pcs-git-lnk] | [![pcs-hub-img]][pcs-hub-lnk] | PHP | PHP Code Sniffer | 190 | | [phplint][plint-git-lnk] | [![plint-hub-img]][plint-hub-lnk] | PHP | PHP Code Linter **[1]** | 191 | | [php-cs-fixer][pcsf-git-lnk] | [![pcsf-hub-img]][pcsf-hub-lnk] | PHP | PHP Coding Standards Fixer | 192 | | [bandit][bandit-git-lnk] | [![bandit-hub-img]][bandit-hub-lnk] | Python | A security linter from PyCQA 193 | | [black][black-git-lnk] | [![black-hub-img]][black-hub-lnk] | Python | The uncompromising Python code formatter | 194 | | [mypy][mypy-git-lnk] | [![mypy-hub-img]][mypy-hub-lnk] | Python | Static source code analysis | 195 | | [pycodestyle][pycs-git-lnk] | [![pycs-hub-img]][pycs-hub-lnk] | Python | Python style guide checker | 196 | | [pydocstyle][pyds-git-lnk] | [![pyds-hub-img]][pyds-hub-lnk] | Python | Python docstyle checker | 197 | | [pylint][pylint-git-lnk] | [![pylint-hub-img]][pylint-hub-lnk] | Python | Python source code, bug and quality checker | 198 | | [terraform-docs][tfdocs-git-lnk] | [![tfdocs-hub-img]][tfdocs-hub-lnk] | Terraform | Terraform doc generator (TF 0.12 ready) **[1]** | 199 | | [terragrunt][tg-git-lnk] | [![tg-hub-img]][tg-hub-lnk] | Terraform | Terragrunt and Terraform | 200 | | [terragrunt-fmt][tgfmt-git-lnk] | [![tgfmt-hub-img]][tgfmt-hub-lnk] | Terraform | `terraform fmt` for Terragrunt files **[1]** | 201 | | [yamlfmt][yfmt-git-lnk] | [![yfmt-hub-img]][yfmt-hub-lnk] | Yaml | Format Yaml files **[1]** | 202 | | [yamllint][ylint-git-lnk] | [![ylint-hub-img]][ylint-hub-lnk] | Yaml | Lint Yaml files | 203 | 204 | > **[1]** Uses a shell wrapper to add **enhanced functionality** not available by original project. 205 | 206 | [aci-git-lnk]: https://github.com/cytopia/awesome-ci 207 | [aci-hub-img]: https://img.shields.io/docker/pulls/cytopia/awesome-ci.svg 208 | [aci-hub-lnk]: https://hub.docker.com/r/cytopia/awesome-ci 209 | 210 | [flint-git-lnk]: https://github.com/cytopia/docker-file-lint 211 | [flint-hub-img]: https://img.shields.io/docker/pulls/cytopia/file-lint.svg 212 | [flint-hub-lnk]: https://hub.docker.com/r/cytopia/file-lint 213 | 214 | [linkcheck-git-lnk]: https://github.com/cytopia/docker-linkcheck 215 | [linkcheck-hub-img]: https://img.shields.io/docker/pulls/cytopia/linkcheck.svg 216 | [linkcheck-hub-lnk]: https://hub.docker.com/r/cytopia/linkcheck 217 | 218 | [jlint-git-lnk]: https://github.com/cytopia/docker-jsonlint 219 | [jlint-hub-img]: https://img.shields.io/docker/pulls/cytopia/jsonlint.svg 220 | [jlint-hub-lnk]: https://hub.docker.com/r/cytopia/jsonlint 221 | 222 | [ansible-git-lnk]: https://github.com/cytopia/docker-ansible 223 | [ansible-hub-img]: https://img.shields.io/docker/pulls/cytopia/ansible.svg 224 | [ansible-hub-lnk]: https://hub.docker.com/r/cytopia/ansible 225 | 226 | [alint-git-lnk]: https://github.com/cytopia/docker-ansible-lint 227 | [alint-hub-img]: https://img.shields.io/docker/pulls/cytopia/ansible-lint.svg 228 | [alint-hub-lnk]: https://hub.docker.com/r/cytopia/ansible-lint 229 | 230 | [kubeval-git-lnk]: https://github.com/cytopia/docker-kubeval 231 | [kubeval-hub-img]: https://img.shields.io/docker/pulls/cytopia/kubeval.svg 232 | [kubeval-hub-lnk]: https://hub.docker.com/r/cytopia/kubeval 233 | 234 | [gfmt-git-lnk]: https://github.com/cytopia/docker-gofmt 235 | [gfmt-hub-img]: https://img.shields.io/docker/pulls/cytopia/gofmt.svg 236 | [gfmt-hub-lnk]: https://hub.docker.com/r/cytopia/gofmt 237 | 238 | [gimp-git-lnk]: https://github.com/cytopia/docker-goimports 239 | [gimp-hub-img]: https://img.shields.io/docker/pulls/cytopia/goimports.svg 240 | [gimp-hub-lnk]: https://hub.docker.com/r/cytopia/goimports 241 | 242 | [glint-git-lnk]: https://github.com/cytopia/docker-golint 243 | [glint-hub-img]: https://img.shields.io/docker/pulls/cytopia/golint.svg 244 | [glint-hub-lnk]: https://hub.docker.com/r/cytopia/golint 245 | 246 | [elint-git-lnk]: https://github.com/cytopia/docker-eslint 247 | [elint-hub-img]: https://img.shields.io/docker/pulls/cytopia/eslint.svg 248 | [elint-hub-lnk]: https://hub.docker.com/r/cytopia/eslint 249 | 250 | [cm-git-lnk]: https://github.com/cytopia/docker-checkmake 251 | [cm-hub-img]: https://img.shields.io/docker/pulls/cytopia/checkmake.svg 252 | [cm-hub-lnk]: https://hub.docker.com/r/cytopia/checkmake 253 | 254 | [pcbf-git-lnk]: https://github.com/cytopia/docker-phpcbf 255 | [pcbf-hub-img]: https://img.shields.io/docker/pulls/cytopia/phpcbf.svg 256 | [pcbf-hub-lnk]: https://hub.docker.com/r/cytopia/phpcbf 257 | 258 | [pcs-git-lnk]: https://github.com/cytopia/docker-phpcs 259 | [pcs-hub-img]: https://img.shields.io/docker/pulls/cytopia/phpcs.svg 260 | [pcs-hub-lnk]: https://hub.docker.com/r/cytopia/phpcs 261 | 262 | [plint-git-lnk]: https://github.com/cytopia/docker-phplint 263 | [plint-hub-img]: https://img.shields.io/docker/pulls/cytopia/phplint.svg 264 | [plint-hub-lnk]: https://hub.docker.com/r/cytopia/phplint 265 | 266 | [pcsf-git-lnk]: https://github.com/cytopia/docker-php-cs-fixer 267 | [pcsf-hub-img]: https://img.shields.io/docker/pulls/cytopia/php-cs-fixer.svg 268 | [pcsf-hub-lnk]: https://hub.docker.com/r/cytopia/php-cs-fixer 269 | 270 | [bandit-git-lnk]: https://github.com/cytopia/docker-bandit 271 | [bandit-hub-img]: https://img.shields.io/docker/pulls/cytopia/bandit.svg 272 | [bandit-hub-lnk]: https://hub.docker.com/r/cytopia/bandit 273 | 274 | [black-git-lnk]: https://github.com/cytopia/docker-black 275 | [black-hub-img]: https://img.shields.io/docker/pulls/cytopia/black.svg 276 | [black-hub-lnk]: https://hub.docker.com/r/cytopia/black 277 | 278 | [mypy-git-lnk]: https://github.com/cytopia/docker-mypy 279 | [mypy-hub-img]: https://img.shields.io/docker/pulls/cytopia/mypy.svg 280 | [mypy-hub-lnk]: https://hub.docker.com/r/cytopia/mypy 281 | 282 | [pycs-git-lnk]: https://github.com/cytopia/docker-pycodestyle 283 | [pycs-hub-img]: https://img.shields.io/docker/pulls/cytopia/pycodestyle.svg 284 | [pycs-hub-lnk]: https://hub.docker.com/r/cytopia/pycodestyle 285 | 286 | [pyds-git-lnk]: https://github.com/cytopia/docker-pydocstyle 287 | [pyds-hub-img]: https://img.shields.io/docker/pulls/cytopia/pydocstyle.svg 288 | [pyds-hub-lnk]: https://hub.docker.com/r/cytopia/pydocstyle 289 | 290 | [pylint-git-lnk]: https://github.com/cytopia/docker-pylint 291 | [pylint-hub-img]: https://img.shields.io/docker/pulls/cytopia/pylint.svg 292 | [pylint-hub-lnk]: https://hub.docker.com/r/cytopia/pylint 293 | 294 | [tfdocs-git-lnk]: https://github.com/cytopia/docker-terraform-docs 295 | [tfdocs-hub-img]: https://img.shields.io/docker/pulls/cytopia/terraform-docs.svg 296 | [tfdocs-hub-lnk]: https://hub.docker.com/r/cytopia/terraform-docs 297 | 298 | [tg-git-lnk]: https://github.com/cytopia/docker-terragrunt 299 | [tg-hub-img]: https://img.shields.io/docker/pulls/cytopia/terragrunt.svg 300 | [tg-hub-lnk]: https://hub.docker.com/r/cytopia/terragrunt 301 | 302 | [tgfmt-git-lnk]: https://github.com/cytopia/docker-terragrunt-fmt 303 | [tgfmt-hub-img]: https://img.shields.io/docker/pulls/cytopia/terragrunt-fmt.svg 304 | [tgfmt-hub-lnk]: https://hub.docker.com/r/cytopia/terragrunt-fmt 305 | 306 | [yfmt-git-lnk]: https://github.com/cytopia/docker-yamlfmt 307 | [yfmt-hub-img]: https://img.shields.io/docker/pulls/cytopia/yamlfmt.svg 308 | [yfmt-hub-lnk]: https://hub.docker.com/r/cytopia/yamlfmt 309 | 310 | [ylint-git-lnk]: https://github.com/cytopia/docker-yamllint 311 | [ylint-hub-img]: https://img.shields.io/docker/pulls/cytopia/yamllint.svg 312 | [ylint-hub-lnk]: https://hub.docker.com/r/cytopia/yamllint 313 | 314 | 315 | ### Makefiles 316 | 317 | Visit **[cytopia/makefiles](https://github.com/cytopia/makefiles)** for dependency-less, seamless project integration and minimum required best-practice code linting for CI. 318 | The provided Makefiles will only require GNU Make and Docker itself removing the need to install anything else. 319 | 320 | 321 | ## :page_facing_up: License 322 | 323 | 324 | **[MIT License](LICENSE)** 325 | 326 | Copyright (c) 2019 [cytopia](https://github.com/cytopia) 327 | --------------------------------------------------------------------------------