├── .github ├── labels.json ├── renovate.json └── workflows │ ├── depup.yml │ ├── labels.yml │ ├── lint.yml │ ├── release.yml │ └── tests.yml ├── .gitignore ├── LICENSE ├── README.md ├── action.yml ├── example-github-pr-check.png ├── example-github-pr-review.png ├── script.sh └── tests ├── init ├── .tflint.hcl └── example.tf ├── modules ├── .tflint.hcl ├── example.tf └── random │ └── main.tf ├── reviewdog-reporters ├── .tflint.hcl └── example.tf └── rulesets ├── .tflint.hcl └── example.tf /.github/labels.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "kind/bug", 4 | "color": "c7def8", 5 | "description": "Categorizes issue or PR as related to a bug" 6 | }, 7 | { 8 | "name": "kind/cleanup", 9 | "color": "c7def8", 10 | "description": "Categorizes issue or PR as related to cleaning up code, process, or technical debt" 11 | }, 12 | { 13 | "name": "kind/documentation", 14 | "color": "c7def8", 15 | "description": "Categorizes issue or PR as related to documentation" 16 | }, 17 | { 18 | "name": "kind/feature", 19 | "color": "c7def8", 20 | "description": "Categorizes issue or PR as related to a new feature" 21 | }, 22 | { 23 | "name": "bump:major", 24 | "color": "ef6bb4", 25 | "description": "Attach to PR to automatically bump major version on merge" 26 | }, 27 | { 28 | "name": "bump:minor", 29 | "color": "ef6bb4", 30 | "description": "Attach to PR to automatically bump minor version on merge" 31 | }, 32 | { 33 | "name": "bump:patch", 34 | "color": "ef6bb4", 35 | "description": "Attach to PR to automatically bump patch version on merge" 36 | }, 37 | { 38 | "name": "do-not-merge/hold", 39 | "color": "e11d21", 40 | "description": "Indicated that a PR is not to be merged" 41 | } 42 | ] 43 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ], 5 | "labels": [ 6 | "bump:patch" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.github/workflows/depup.yml: -------------------------------------------------------------------------------- 1 | name: depup 2 | 3 | on: 4 | schedule: 5 | - cron: '14 14 * * *' # Runs at 14:14 UTC every day 6 | repository_dispatch: 7 | types: [depup] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | reviewdog: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 15 | - uses: reviewdog/action-depup/with-pr@94a1aaf4e4923064019214b48a43276218af7ad5 # v1.6.4 16 | with: 17 | file: action.yml 18 | version_name: REVIEWDOG_VERSION 19 | repo: reviewdog/reviewdog 20 | labels: "bump:minor" 21 | 22 | tflint-ruleset-azurerm: 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 26 | - uses: reviewdog/action-depup/with-pr@94a1aaf4e4923064019214b48a43276218af7ad5 # v1.6.4 27 | with: 28 | file: tests/init/.tflint.hcl 29 | version_name: version 30 | repo: terraform-linters/tflint-ruleset-azurerm 31 | -------------------------------------------------------------------------------- /.github/workflows/labels.yml: -------------------------------------------------------------------------------- 1 | name: Labels 2 | 3 | on: 4 | push: 5 | paths: 6 | - .github/labels.json 7 | - .github/workflows/labels.yml 8 | branches: 9 | - master 10 | 11 | jobs: 12 | Manage: 13 | name: Manage GitHub labels 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@master 19 | 20 | - name: Manage labels 21 | uses: lannonbr/issue-label-manager-action@e8dbcd8198e86a1e98d5372e55db976fed9ba6f7 # 4.0.0 22 | env: 23 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 24 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | 9 | jobs: 10 | Shellcheck: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 15 | 16 | - uses: haya14busa/action-cond@94f77f7a80cd666cb3155084e428254fea4281fd # v1.2.1 17 | id: reporter 18 | with: 19 | cond: ${{ github.event_name == 'pull_request' }} 20 | if_true: "github-pr-review" 21 | if_false: "github-check" 22 | 23 | - uses: reviewdog/action-shellcheck@6e0e63d1750d02d761b3df0f2c5ba9f9ac4a9ed7 # v1.29.0 24 | with: 25 | github_token: ${{ secrets.github_token }} 26 | reporter: ${{ steps.reporter.outputs.value }} 27 | level: warning 28 | 29 | Misspell: 30 | runs-on: ubuntu-latest 31 | 32 | steps: 33 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 34 | 35 | - uses: reviewdog/action-misspell@9daa94af4357dddb6fd3775de806bc0a8e98d3e4 # v1.26.3 36 | with: 37 | github_token: ${{ secrets.github_token }} 38 | reporter: github-check 39 | level: warning 40 | locale: "US" 41 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | on: 3 | push: 4 | branches: 5 | - master 6 | tags: 7 | - 'v*.*.*' 8 | pull_request: 9 | types: 10 | - labeled 11 | 12 | jobs: 13 | release: 14 | if: github.event.action != 'labeled' 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 18 | 19 | # Bump version on merging Pull Requests with specific labels. 20 | # (bump:major,bump:minor,bump:patch) 21 | - id: bumpr 22 | if: "!startsWith(github.ref, 'refs/tags/')" 23 | uses: haya14busa/action-bumpr@78ab5a104d20896c9c9122c64221b3aecf1a8cbb # v1.10.0 24 | 25 | # Update corresponding major and minor tag. 26 | # e.g. Update v1 and v1.2 when releasing v1.2.3 27 | - uses: haya14busa/action-update-semver@fb48464b2438ae82cc78237be61afb4f461265a1 # v1.2.1 28 | if: "!steps.bumpr.outputs.skip" 29 | with: 30 | tag: ${{ steps.bumpr.outputs.next_version }} 31 | 32 | # Get tag name. 33 | - id: tag 34 | uses: haya14busa/action-cond@94f77f7a80cd666cb3155084e428254fea4281fd # v1.2.1 35 | with: 36 | cond: "${{ startsWith(github.ref, 'refs/tags/') }}" 37 | if_true: ${{ github.ref }} 38 | if_false: ${{ steps.bumpr.outputs.next_version }} 39 | 40 | # Create release 41 | - if: "steps.tag.outputs.value != ''" 42 | env: 43 | TAG_NAME: ${{ steps.tag.outputs.value }} 44 | CURRENT: ${{ steps.bumpr.outputs.current_version }} 45 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 46 | run: | 47 | gh release create "${TAG_NAME}" -t "Release ${TAG_NAME/refs\/tags\//}" --generate-notes --notes-start-tag "${CURRENT}" 48 | 49 | release-check: 50 | if: github.event.action == 'labeled' 51 | runs-on: ubuntu-latest 52 | steps: 53 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 54 | - name: Post bumpr status comment 55 | uses: haya14busa/action-bumpr@78ab5a104d20896c9c9122c64221b3aecf1a8cbb # v1.10.0 56 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | 9 | defaults: 10 | run: 11 | shell: bash 12 | 13 | jobs: 14 | test-check: 15 | name: tflint (github-check) 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 20 | 21 | - uses: ./ 22 | continue-on-error: true 23 | id: test 24 | with: 25 | github_token: ${{ secrets.github_token }} 26 | reporter: github-check 27 | level: info 28 | working_directory: tests/reviewdog-reporters 29 | 30 | # The check is expected to fail on the test data 31 | - name: Check return codes 32 | if: success() || failure () 33 | run: | 34 | tflint_return="${{ steps.test.outputs.tflint-return-code }}" 35 | reviewdog_return="${{ steps.test.outputs.reviewdog-return-code }}" 36 | 37 | if [[ "$tflint_return" -eq 2 ]]; then 38 | echo "tflint correctly returned failure ${tflint_return}" 39 | else 40 | echo "tflint returned ${tflint_return}, expected '2'. Failing..." 41 | exit 1 42 | fi 43 | 44 | if [[ "$reviewdog_return" -eq 0 ]]; then 45 | echo "reviewdog correctly returned success: ${reviewdog_return}" 46 | else 47 | echo "reviewdog returned ${reviewdog_return}, expected '0'. Failing..." 48 | exit 1 49 | fi 50 | 51 | test-pr-check: 52 | if: github.event_name == 'pull_request' 53 | name: tflint (github-pr-check) 54 | runs-on: ubuntu-latest 55 | 56 | steps: 57 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 58 | 59 | - uses: ./ 60 | continue-on-error: true 61 | id: test 62 | with: 63 | github_token: ${{ secrets.github_token }} 64 | reporter: github-pr-check 65 | level: info 66 | working_directory: tests/reviewdog-reporters 67 | 68 | # The check is expected to fail on the test data 69 | - name: Check return codes 70 | if: success() || failure () 71 | run: | 72 | tflint_return="${{ steps.test.outputs.tflint-return-code }}" 73 | reviewdog_return="${{ steps.test.outputs.reviewdog-return-code }}" 74 | 75 | if [[ "$tflint_return" -eq 2 ]]; then 76 | echo "tflint correctly returned failure ${tflint_return}" 77 | else 78 | echo "tflint returned ${tflint_return}, expected '2'. Failing..." 79 | exit 1 80 | fi 81 | 82 | if [ "$reviewdog_return" -eq 0 ]; then 83 | echo "reviewdog correctly returned success: ${reviewdog_return}" 84 | else 85 | echo "reviewdog returned ${reviewdog_return}, expected '0'. Failing..." 86 | exit 1 87 | fi 88 | 89 | test-pr-review: 90 | if: github.event_name == 'pull_request' 91 | name: tflint (github-pr-review) 92 | runs-on: ubuntu-latest 93 | 94 | steps: 95 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 96 | 97 | - uses: ./ 98 | continue-on-error: true 99 | id: test 100 | with: 101 | github_token: ${{ secrets.github_token }} 102 | reporter: github-pr-review 103 | level: info 104 | working_directory: tests/reviewdog-reporters 105 | 106 | # The check is expected to fail on the test data 107 | # but for forked PRs reviewdog will just output 108 | # on the PR and report success 109 | - name: Check return codes 110 | if: success() || failure () 111 | run: | 112 | tflint_return="${{ steps.test.outputs.tflint-return-code }}" 113 | reviewdog_return="${{ steps.test.outputs.reviewdog-return-code }}" 114 | 115 | if [[ "$tflint_return" -eq 2 ]]; then 116 | echo "tflint correctly returned failure ${tflint_return}" 117 | else 118 | echo "tflint returned ${tflint_return}, expected '2'. Failing..." 119 | exit 1 120 | fi 121 | 122 | if [ "$reviewdog_return" -eq 0 ]; then 123 | echo "reviewdog correctly returned failure: ${reviewdog_return}" 124 | else 125 | echo "reviewdog returned ${reviewdog_return}, expected '0'. Failing..." 126 | exit 1 127 | fi 128 | 129 | test-rulesets: 130 | name: tflint (rulesets) 131 | runs-on: ubuntu-latest 132 | 133 | steps: 134 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 135 | 136 | - uses: ./ 137 | continue-on-error: true 138 | id: test 139 | with: 140 | github_token: ${{ secrets.github_token }} 141 | reporter: github-check 142 | level: info 143 | tflint_rulesets: "azurerm google" 144 | working_directory: tests/rulesets 145 | 146 | # The check is expected to fail on the test data 147 | - name: Check return codes 148 | if: success() || failure () 149 | run: | 150 | tflint_return="${{ steps.test.outputs.tflint-return-code }}" 151 | reviewdog_return="${{ steps.test.outputs.reviewdog-return-code }}" 152 | 153 | if [[ "$tflint_return" -eq 2 ]]; then 154 | echo "tflint correctly returned failure ${tflint_return}" 155 | else 156 | echo "tflint returned ${tflint_return}, expected '2'. Failing..." 157 | exit 1 158 | fi 159 | 160 | if [[ "$reviewdog_return" -eq 0 ]]; then 161 | echo "reviewdog correctly returned success: ${reviewdog_return}" 162 | else 163 | echo "reviewdog returned ${reviewdog_return}, expected '0'. Failing..." 164 | exit 1 165 | fi 166 | 167 | test-init: 168 | name: tflint (init) 169 | runs-on: ubuntu-latest 170 | 171 | steps: 172 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 173 | - uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3.1.2 174 | 175 | - run: terraform init 176 | working-directory: tests/init 177 | 178 | - uses: ./ 179 | continue-on-error: true 180 | id: test 181 | with: 182 | github_token: ${{ secrets.github_token }} 183 | reporter: github-check 184 | level: info 185 | tflint_init: true 186 | working_directory: tests/init 187 | 188 | # The check is expected to fail on the test data 189 | - name: Check return codes 190 | if: success() || failure () 191 | run: | 192 | tflint_return="${{ steps.test.outputs.tflint-return-code }}" 193 | reviewdog_return="${{ steps.test.outputs.reviewdog-return-code }}" 194 | if [[ "$tflint_return" -eq 2 ]]; then 195 | echo "tflint correctly returned failure ${tflint_return}" 196 | else 197 | echo "tflint returned ${tflint_return}, expected '2'. Failing..." 198 | exit 1 199 | fi 200 | if [[ "$reviewdog_return" -eq 0 ]]; then 201 | echo "reviewdog correctly returned success: ${reviewdog_return}" 202 | else 203 | echo "reviewdog returned ${reviewdog_return}, expected '0'. Failing..." 204 | exit 1 205 | fi 206 | 207 | test-modules: 208 | if: github.event_name == 'pull_request' 209 | name: tflint (modules) 210 | runs-on: ubuntu-latest 211 | 212 | steps: 213 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 214 | - uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3.1.2 215 | 216 | - run: terraform init 217 | working-directory: tests/modules 218 | 219 | - uses: ./ 220 | continue-on-error: true 221 | id: test 222 | with: 223 | github_token: ${{ secrets.github_token }} 224 | reporter: github-pr-check 225 | level: info 226 | working_directory: tests/modules 227 | 228 | # The check is expected to fail on the test data 229 | - name: Check return codes 230 | if: success() || failure () 231 | run: | 232 | tflint_return="${{ steps.test.outputs.tflint-return-code }}" 233 | reviewdog_return="${{ steps.test.outputs.reviewdog-return-code }}" 234 | if [[ "$tflint_return" -eq 2 ]]; then 235 | echo "tflint correctly returned failure ${tflint_return}" 236 | else 237 | echo "tflint returned ${tflint_return}, expected '2'. Failing..." 238 | exit 1 239 | fi 240 | if [[ "$reviewdog_return" -eq 0 ]]; then 241 | echo "reviewdog correctly returned success: ${reviewdog_return}" 242 | else 243 | echo "reviewdog returned ${reviewdog_return}, expected '0'. Failing..." 244 | exit 1 245 | fi 246 | 247 | test-operating-systems: 248 | strategy: 249 | matrix: 250 | platform: [ubuntu-latest, macos-latest, windows-latest] 251 | name: tflint (${{ matrix.platform }}) 252 | runs-on: ${{ matrix.platform }} 253 | 254 | steps: 255 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 256 | 257 | - uses: ./ 258 | continue-on-error: true 259 | id: test 260 | with: 261 | github_token: ${{ secrets.github_token }} 262 | reporter: github-check 263 | level: info 264 | working_directory: tests/reviewdog-reporters 265 | 266 | # The check is expected to fail on the test data 267 | - name: Check return codes 268 | if: success() || failure () 269 | run: | 270 | tflint_return="${{ steps.test.outputs.tflint-return-code }}" 271 | reviewdog_return="${{ steps.test.outputs.reviewdog-return-code }}" 272 | 273 | if [[ "$tflint_return" -eq 2 ]]; then 274 | echo "tflint correctly returned failure ${tflint_return}" 275 | else 276 | echo "tflint returned ${tflint_return}, expected '2'. Failing..." 277 | exit 1 278 | fi 279 | 280 | if [ "$reviewdog_return" -eq 0 ]; then 281 | echo "reviewdog correctly returned success: ${reviewdog_return}" 282 | else 283 | echo "reviewdog returned ${reviewdog_return}, expected '0'. Failing..." 284 | exit 1 285 | fi 286 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # macOS 2 | .DS_Store 3 | .DS_Store? 4 | **/.DS_Store 5 | ._* 6 | .Spotlight-V100 7 | .Trashes 8 | ehthumbs.db 9 | Thumbs.db 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 reviewdog 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GitHub Action: Run tflint with reviewdog 2 | 3 | [![Tests](https://github.com/reviewdog/action-tflint/workflows/Tests/badge.svg)](https://github.com/reviewdog/action-tflint/actions?query=workflow%3ATests) 4 | [![Lint](https://github.com/reviewdog/action-tflint/workflows/Lint/badge.svg)](https://github.com/reviewdog/action-tflint/actions?query=workflow%3ALint) 5 | [![release](https://github.com/reviewdog/action-tflint/workflows/release/badge.svg)](https://github.com/reviewdog/action-tflint/actions?query=workflow%3Arelease) 6 | [![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/reviewdog/action-tflint?logo=github&sort=semver)](https://github.com/reviewdog/action-tflint/releases) 7 | [![action-bumpr supported](https://img.shields.io/badge/bumpr-supported-ff69b4?logo=github&link=https://github.com/haya14busa/action-bumpr)](https://github.com/haya14busa/action-bumpr) 8 | [![depup](https://github.com/reviewdog/action-tflint/workflows/depup/badge.svg)](https://github.com/reviewdog/action-tflint/actions?query=workflow%3Adepup) 9 | 10 | This action runs [tflint](https://github.com/wata727/tflint) with 11 | [reviewdog](https://github.com/reviewdog/reviewdog) on pull requests 12 | to enforce best practices. 13 | 14 | ## Examples 15 | 16 | ### With `github-pr-check` 17 | 18 | By default, with `reporter: github-pr-check` an annotation is added to 19 | the line: 20 | 21 | ![Example comment made by the action, with github-pr-check](./example-github-pr-check.png) 22 | 23 | ### With `github-pr-review` 24 | 25 | With `reporter: github-pr-review` a comment is added to 26 | the Pull Request Conversation: 27 | 28 | ![Example comment made by the action, with github-pr-review](./example-github-pr-review.png) 29 | 30 | ## Inputs 31 | 32 | ### `github_token` 33 | 34 | **Required**. Must be in form of `github_token: ${{ secrets.github_token }}`. 35 | 36 | ### `level` 37 | 38 | Optional. Report level for reviewdog [`info`,`warning`,`error`]. 39 | It's same as `-level` flag of reviewdog. 40 | The default is `error`. 41 | 42 | ### `reporter` 43 | 44 | Optional. Reporter of reviewdog command [`github-pr-check`,`github-pr-review`]. 45 | The default is `github-pr-check`. 46 | 47 | ### `filter_mode` 48 | 49 | Optional. Filtering for the reviewdog command [`added`,`diff_context`,`file`,`nofilter`]. 50 | 51 | The default is `added`. 52 | 53 | See [reviewdog documentation for filter mode](https://github.com/reviewdog/reviewdog/tree/master#filter-mode) for details. 54 | 55 | ### `fail_level` 56 | 57 | Optional. If set to `none`, always use exit code 0 for reviewdog. 58 | Otherwise, exit code 1 for reviewdog if it finds at least 1 issue with severity greater than or equal to the given level. 59 | Possible values: [`none`, `any`, `info`, `warning`, `error`] 60 | Default is `none`. 61 | 62 | ### `fail_on_error` 63 | 64 | Deprecated, use `fail_level` instead. 65 | Optional. Exit code for reviewdog when errors are found [`true`,`false`]. 66 | 67 | The default is `false`. 68 | 69 | See [reviewdog documentation for exit codes](https://github.com/reviewdog/reviewdog/tree/master#exit-codes) for details. 70 | 71 | ### `working_directory` 72 | 73 | Optional. Directory to run the action on, from the repo root. 74 | The default is `.` ( root of the repository). 75 | 76 | ### `tflint_version` 77 | 78 | Optional. The tflint version to install and use. 79 | The default is `latest`. 80 | 81 | ### `tflint_rulesets` 82 | 83 | Optional. Space separated, official (from the terraform-linters GitHub organization) tflint rulesets to install and use. If a pre-configured `TFLINT_PLUGIN_DIR` is set, rulesets are installed in that directory. 84 | Default is `` (empty). 85 | 86 | ### `tflint_init` 87 | 88 | Optional. Whether to run `tflint --init` prior to linting (useful if you have a .tflint.hcl with some values in it). 89 | The default is `false`. 90 | 91 | ### `tflint_target_dir` 92 | 93 | Optional. The target dir for the tflint command. This is the directory passed to tflint as opposed to working_directory which is the directory the command is executed from. 94 | The default is `.`. 95 | 96 | ### `tflint_config` 97 | 98 | Optional. Config file name for tflint. 99 | The default is `.tflint.hcl` 100 | 101 | ### `flags` 102 | 103 | Optional. List of arguments to send to `tflint`. 104 | For the output to be parsable by reviewdog [`--format=checkstyle` is enforced](./entrypoint.sh). 105 | The default is `--call-module-type=all`. 106 | 107 | ## Outputs 108 | 109 | ## `tflint-return-code` 110 | 111 | The `tflint` command return code. 112 | 113 | ## `reviewdog-return-code` 114 | 115 | The `reviewdog` command return code. 116 | 117 | ## Example usage 118 | 119 | ```yml 120 | name: reviewdog 121 | on: [pull_request] 122 | jobs: 123 | tflint: 124 | name: runner / tflint 125 | runs-on: ubuntu-latest 126 | 127 | steps: 128 | - name: Clone repo 129 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 130 | 131 | # Install latest Terraform manually as 132 | # Docker-based GitHub Actions are 133 | # slow due to lack of caching 134 | # Note: Terraform is not needed for tflint 135 | - name: Install Terraform 136 | run: | 137 | brew install terraform 138 | 139 | # Run init to get module code to be able to use `--call-module-type=all` 140 | - name: Terraform init 141 | run: | 142 | terraform init 143 | 144 | # Minimal example 145 | - name: tflint 146 | uses: reviewdog/action-tflint@41b4770c9d9e50741c20e431986b33124a07ca52 # v1.24.2 147 | with: 148 | github_token: ${{ secrets.github_token }} 149 | 150 | # More complex example 151 | - name: tflint 152 | uses: reviewdog/action-tflint@41b4770c9d9e50741c20e431986b33124a07ca52 # v1.24.2 153 | with: 154 | github_token: ${{ secrets.github_token }} 155 | working_directory: "testdata" # Optional. Change working directory 156 | reporter: github-pr-review # Optional. Change reporter 157 | fail_level: "any" # Optional. Fail action if it finds at least 1 issue with severity greater than or equal to the given level. 158 | filter_mode: "nofilter" # Optional. Check all files, not just the diff 159 | tflint_version: "v0.24.0" # Optional. Custom version, instead of latest 160 | tflint_rulesets: "azurerm google" # Optional. Extra official rulesets to install 161 | flags: "--call-module-type=all" # Optional. Add custom tflint flags 162 | ``` 163 | 164 | ## Development 165 | 166 | ### Release 167 | 168 | #### [haya14busa/action-bumpr](https://github.com/haya14busa/action-bumpr) 169 | 170 | You can bump version on merging Pull Requests with specific labels (bump:major,bump:minor,bump:patch). 171 | Pushing tag manually by yourself also work. 172 | 173 | #### [haya14busa/action-update-semver](https://github.com/haya14busa/action-update-semver) 174 | 175 | This action updates major/minor release tags on a tag push. e.g. Update v1 and v1.2 tag when released v1.2.3. 176 | ref: https://help.github.com/en/articles/about-actions#versioning-your-action 177 | 178 | ### Lint - reviewdog integration 179 | 180 | This reviewdog action template itself is integrated with reviewdog to run lints 181 | which is useful for Docker container based actions. 182 | 183 | Supported linters: 184 | 185 | - [reviewdog/action-shellcheck](https://github.com/reviewdog/action-shellcheck) 186 | - [reviewdog/action-hadolint](https://github.com/reviewdog/action-hadolint) 187 | - [reviewdog/action-misspell](https://github.com/reviewdog/action-misspell) 188 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: 'Run tflint with reviewdog' 2 | description: '🐶 Run tflint with reviewdog on pull requests to enforce best practices' 3 | author: 'vlaaaaaaad (reviewdog)' 4 | 5 | inputs: 6 | github_token: 7 | description: 'GITHUB_TOKEN' 8 | required: true 9 | default: ${{ github.token }} 10 | level: 11 | description: 'Report level for reviewdog [info,warning,error]' 12 | default: 'error' 13 | reporter: 14 | description: | 15 | Reporter of reviewdog command [github-pr-check,github-pr-review]. 16 | Default is github-pr-check. 17 | default: 'github-pr-check' 18 | filter_mode: 19 | description: | 20 | Filtering for the reviewdog command [added,diff_context,file,nofilter]. 21 | Default is added. 22 | default: 'added' 23 | fail_level: 24 | description: | 25 | If set to `none`, always use exit code 0 for reviewdog. 26 | Otherwise, exit code 1 for reviewdog if it finds at least 1 issue with severity greater than or equal to the given level. 27 | Possible values: [none,any,info,warning,error] 28 | Default is `none`. 29 | default: 'none' 30 | fail_on_error: 31 | description: | 32 | Deprecated, use `fail_level` instead. 33 | Exit code for reviewdog when errors are found [true,false] 34 | Default is `false`. 35 | deprecationMessage: Deprecated, use `fail_level` instead. 36 | default: 'false' 37 | working_directory: 38 | description: | 39 | Directory to run the action on, from the repo root. 40 | Default is . ( root of the repository) 41 | default: '.' 42 | tflint_version: 43 | description: | 44 | The tflint version to install and use. 45 | Default is to use the latest release version. 46 | default: 'latest' 47 | tflint_rulesets: 48 | description: | 49 | Space separated, official (from the terraform-linters GitHub organization) tflint rulesets to install and use. If a pre-configured `TFLINT_PLUGIN_DIR` is set, rulesets are installed in that directory. 50 | Default is empty. 51 | default: '' 52 | tflint_init: 53 | description: | 54 | Whether or not to run tflint --init prior to running scan [true,false] 55 | Default is `false`. 56 | default: 'false' 57 | tflint_target_dir: 58 | description: | 59 | The target dir for the tflint command. This is the directory passed to tflint as opposed to working_directory which is the directory the command is executed from. 60 | Default is . ( root of the repository) 61 | default: '.' 62 | tflint_config: 63 | description: | 64 | Config file name for tflint. 65 | Default is `.tflint.hcl`. 66 | default: '.tflint.hcl' 67 | flags: 68 | description: | 69 | List of arguments to send to tflint 70 | For the output to be parsable by reviewdog --format=checkstyle is enforced 71 | Default is --call-module-type=all. 72 | default: '--call-module-type=all' 73 | 74 | outputs: 75 | tflint-return-code: 76 | description: 'tflint command return code' 77 | value: ${{ steps.tflint.outputs.tflint-return-code }} 78 | reviewdog-return-code: 79 | description: 'reviewdog command return code' 80 | value: ${{ steps.tflint.outputs.reviewdog-return-code }} 81 | 82 | runs: 83 | using: 'composite' 84 | steps: 85 | - run: $GITHUB_ACTION_PATH/script.sh 86 | id: tflint 87 | shell: bash 88 | env: 89 | # We may want to allow specifying reviewdog version as 90 | # action's input, but let's start with hard coded latest stable version for reviewdog 91 | REVIEWDOG_VERSION: v0.20.2 92 | # INPUT_ is not available in Composite run steps 93 | # https://github.community/t/input-variable-name-is-not-available-in-composite-run-steps/127611 94 | INPUT_GITHUB_TOKEN: ${{ inputs.github_token }} 95 | INPUT_LEVEL: ${{ inputs.level }} 96 | INPUT_REPORTER: ${{ inputs.reporter }} 97 | INPUT_FILTER_MODE: ${{ inputs.filter_mode }} 98 | INPUT_FAIL_LEVEL: ${{ inputs.fail_level }} 99 | INPUT_FAIL_ON_ERROR: ${{ inputs.fail_on_error }} 100 | INPUT_WORKING_DIRECTORY: ${{ inputs.working_directory }} 101 | INPUT_TFLINT_VERSION: ${{ inputs.tflint_version }} 102 | INPUT_TFLINT_RULESETS: ${{ inputs.tflint_rulesets }} 103 | INPUT_TFLINT_INIT: ${{ inputs.tflint_init }} 104 | INPUT_TFLINT_TARGET_DIR: ${{ inputs.tflint_target_dir }} 105 | INPUT_TFLINT_CONFIG: ${{ inputs.tflint_config }} 106 | INPUT_FLAGS: ${{ inputs.flags }} 107 | 108 | branding: 109 | icon: 'edit' 110 | color: 'gray-dark' 111 | -------------------------------------------------------------------------------- /example-github-pr-check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reviewdog/action-tflint/92ecd5bdf3d31ada4ac26a702666986f67385fda/example-github-pr-check.png -------------------------------------------------------------------------------- /example-github-pr-review.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reviewdog/action-tflint/92ecd5bdf3d31ada4ac26a702666986f67385fda/example-github-pr-review.png -------------------------------------------------------------------------------- /script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Fail fast on errors, unset variables, and failures in piped commands 4 | set -Eeuo pipefail 5 | 6 | cd "${GITHUB_WORKSPACE}/${INPUT_WORKING_DIRECTORY}" || exit 7 | 8 | echo '::group::Preparing' 9 | unameOS="$(uname -s)" 10 | case "${unameOS}" in 11 | Linux*) os=linux;; 12 | Darwin*) os=darwin;; 13 | CYGWIN*) os=windows;; 14 | MINGW*) os=windows;; 15 | MSYS_NT*) os=windows;; 16 | *) echo "Unknown system: ${unameOS}" && exit 1 17 | esac 18 | 19 | unameArch="$(uname -m)" 20 | case "${unameArch}" in 21 | x86*) arch=amd64;; 22 | aarch64*) arch=arm64;; 23 | arm64*) arch=arm64;; 24 | *) echo "Unsupported architecture: ${unameArch}" && exit 1 25 | esac 26 | 27 | TEMP_PATH="$(mktemp -d)" 28 | echo "Detected ${os} running on ${arch}, will install tools in ${TEMP_PATH}" 29 | REVIEWDOG_PATH="${TEMP_PATH}/reviewdog" 30 | TFLINT_PATH="${TEMP_PATH}/tflint" 31 | 32 | if [[ -z "${INPUT_TFLINT_VERSION}" ]] || [[ "${INPUT_TFLINT_VERSION}" == "latest" ]]; then 33 | echo "Looking up the latest tflint version ..." 34 | tflint_version=$(curl -H "Authorization: Bearer ${INPUT_GITHUB_TOKEN}" --silent --show-error --fail --location "https://api.github.com/repos/terraform-linters/tflint/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') 35 | else 36 | tflint_version=${INPUT_TFLINT_VERSION} 37 | fi 38 | 39 | if [[ -z "${TFLINT_PLUGIN_DIR:-}" ]]; then 40 | export TFLINT_PLUGIN_DIR="${TFLINT_PATH}/.tflint.d/plugins" 41 | mkdir -p "${TFLINT_PLUGIN_DIR}" 42 | else 43 | echo "Found pre-configured TFLINT_PLUGIN_DIR=${TFLINT_PLUGIN_DIR}" 44 | fi 45 | echo '::endgroup::' 46 | 47 | echo "::group::🐶 Installing reviewdog (${REVIEWDOG_VERSION}) ... https://github.com/reviewdog/reviewdog" 48 | curl -sfL https://raw.githubusercontent.com/reviewdog/reviewdog/fd59714416d6d9a1c0692d872e38e7f8448df4fc/install.sh | sh -s -- -b "${REVIEWDOG_PATH}" "${REVIEWDOG_VERSION}" 2>&1 49 | echo '::endgroup::' 50 | 51 | echo "::group:: Installing tflint (${tflint_version}) ... https://github.com/terraform-linters/tflint" 52 | curl --silent --show-error --fail \ 53 | --location "https://github.com/terraform-linters/tflint/releases/download/${tflint_version}/tflint_${os}_${arch}.zip" \ 54 | --output "${TEMP_PATH}/tflint.zip" 55 | 56 | unzip -u "${TEMP_PATH}/tflint.zip" -d "${TEMP_PATH}/temp-tflint" 57 | test ! -d "${TFLINT_PATH}" && install -d "${TFLINT_PATH}" 58 | install "${TEMP_PATH}/temp-tflint/tflint" "${TFLINT_PATH}" 59 | rm -rf "${TEMP_PATH}/tflint.zip" "${TEMP_PATH}/temp-tflint" 60 | echo '::endgroup::' 61 | 62 | for RULESET in ${INPUT_TFLINT_RULESETS}; do 63 | PLUGIN="tflint-ruleset-${RULESET}" 64 | REPOSITORY="https://github.com/terraform-linters/${PLUGIN}" 65 | 66 | echo "::group:: Installing tflint plugin for ${RULESET} (latest) ... ${REPOSITORY}" 67 | curl --silent --show-error --fail \ 68 | --location "${REPOSITORY}"/releases/latest/download/"${PLUGIN}"_"${os}"_"${arch}".zip \ 69 | --output "${PLUGIN}".zip \ 70 | && unzip "${PLUGIN}".zip -d "${TFLINT_PLUGIN_DIR}" && rm "${PLUGIN}".zip 71 | echo '::endgroup::' 72 | done 73 | 74 | case "${INPUT_TFLINT_INIT:-false}" in 75 | true) 76 | echo "::group:: Initialize tflint from local configuration" 77 | TFLINT_PLUGIN_DIR="${TFLINT_PLUGIN_DIR}" GITHUB_TOKEN="${INPUT_GITHUB_TOKEN}" "${TFLINT_PATH}/tflint" --init -c "${INPUT_TFLINT_CONFIG}" 78 | echo "::endgroup::" 79 | ;; 80 | false) 81 | true # do nothing 82 | ;; 83 | *) 84 | echo "::group:: Initialize tflint from local configuration" 85 | echo "Unknown option provided for tflint_init: ${INPUT_TFLINT_INIT}. Value must be one of ['true', 'false']." 86 | echo "::endgroup::" 87 | ;; 88 | esac 89 | 90 | echo "::group:: Print tflint details ..." 91 | "${TFLINT_PATH}/tflint" --version -c "${INPUT_TFLINT_CONFIG}" 92 | echo '::endgroup::' 93 | 94 | 95 | echo '::group:: Running tflint with reviewdog 🐶 ...' 96 | export REVIEWDOG_GITHUB_API_TOKEN="${INPUT_GITHUB_TOKEN}" 97 | 98 | # Allow failures now, as reviewdog handles them 99 | set +Eeuo pipefail 100 | 101 | 102 | # We only want to specify the tflint target directory if it is not the default to avoid conflicts 103 | CHDIR_COMMAND="" 104 | if [ "$INPUT_TFLINT_TARGET_DIR" == "." ]; then 105 | echo "Using default working directory. No need to specify chdir" 106 | else 107 | echo "Custom target directory specified." 108 | CHDIR_COMMAND="--chdir=${INPUT_TFLINT_TARGET_DIR}" 109 | fi 110 | 111 | # shellcheck disable=SC2086 112 | TFLINT_PLUGIN_DIR=${TFLINT_PLUGIN_DIR} "${TFLINT_PATH}/tflint" -c "${INPUT_TFLINT_CONFIG}" --format=checkstyle ${INPUT_FLAGS} ${CHDIR_COMMAND} \ 113 | | "${REVIEWDOG_PATH}/reviewdog" -f=checkstyle \ 114 | -name="tflint" \ 115 | -reporter="${INPUT_REPORTER}" \ 116 | -level="${INPUT_LEVEL}" \ 117 | -fail-level="${INPUT_FAIL_LEVEL}" \ 118 | -fail-on-error="${INPUT_FAIL_ON_ERROR}" \ 119 | -filter-mode="${INPUT_FILTER_MODE}" 120 | 121 | tflint_return="${PIPESTATUS[0]}" reviewdog_return="${PIPESTATUS[1]}" exit_code=$? 122 | echo "tflint-return-code=${tflint_return}" >> "${GITHUB_OUTPUT}" 123 | echo "reviewdog-return-code=${reviewdog_return}" >> "${GITHUB_OUTPUT}" 124 | echo '::endgroup::' 125 | 126 | exit "${exit_code}" 127 | -------------------------------------------------------------------------------- /tests/init/.tflint.hcl: -------------------------------------------------------------------------------- 1 | plugin "azurerm" { 2 | enabled = true 3 | version = "0.25.1" 4 | source = "github.com/terraform-linters/tflint-ruleset-azurerm" 5 | } 6 | -------------------------------------------------------------------------------- /tests/init/example.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_virtual_machine" "foo" { 2 | vm_size = "Standard_DS1_v3" # invalid type! 3 | } 4 | -------------------------------------------------------------------------------- /tests/modules/.tflint.hcl: -------------------------------------------------------------------------------- 1 | plugin "terraform" { 2 | enabled = true 3 | preset = "recommended" 4 | } 5 | -------------------------------------------------------------------------------- /tests/modules/example.tf: -------------------------------------------------------------------------------- 1 | module "random_name" { 2 | source = "./random" 3 | } 4 | 5 | resource "aws_instance" "foo" { 6 | ami = "ami-0ff8a91507f77f867" 7 | instance_type = "t1.2xlarge" # invalid type! 8 | 9 | tags = { 10 | Name = module.random_name.result 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/modules/random/main.tf: -------------------------------------------------------------------------------- 1 | resource "random_string" "this" { 2 | length = 16 3 | } 4 | 5 | output "result" { 6 | value = random_string.this.result 7 | } 8 | -------------------------------------------------------------------------------- /tests/reviewdog-reporters/.tflint.hcl: -------------------------------------------------------------------------------- 1 | plugin "terraform" { 2 | enabled = true 3 | preset = "recommended" 4 | } 5 | -------------------------------------------------------------------------------- /tests/reviewdog-reporters/example.tf: -------------------------------------------------------------------------------- 1 | resource "aws_instance" "foo" { 2 | ami = "ami-0ff8a91507f77f867" 3 | instance_type = "t1.2xlarge" # invalid type! 4 | } 5 | -------------------------------------------------------------------------------- /tests/rulesets/.tflint.hcl: -------------------------------------------------------------------------------- 1 | plugin "azurerm" { 2 | enabled = true 3 | } 4 | -------------------------------------------------------------------------------- /tests/rulesets/example.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_virtual_machine" "foo" { 2 | vm_size = "Standard_DS1_v3" # invalid type! 3 | } 4 | --------------------------------------------------------------------------------