diff --git a/.github/workflows/depup.yml b/.github/workflows/depup.yml index 82d060b..0760d6a 100644 --- a/.github/workflows/depup.yml +++ b/.github/workflows/depup.yml @@ -16,7 +16,7 @@ jobs: - uses: haya14busa/action-depup@v1 id: depup with: - file: Dockerfile + file: action.yml version_name: REVIEWDOG_VERSION repo: reviewdog/reviewdog @@ -33,4 +33,4 @@ jobs: This PR is auto generated by [depup workflow](https://github.com/${{ github.repository }}/actions?query=workflow%3Adepup). branch: depup/reviewdog base: master - labels: "bump:minor" \ No newline at end of file + labels: "bump:minor" diff --git a/.github/workflows/dockerimage.yml b/.github/workflows/dockerimage.yml deleted file mode 100644 index e70018a..0000000 --- a/.github/workflows/dockerimage.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Docker Image CI - -on: - push: - branches: - - master - pull_request: - -jobs: - - build: - - runs-on: ubuntu-latest - - steps: - - name: Clone repo - uses: actions/checkout@master - - - name: Build the Docker image - run: docker build . --file Dockerfile --tag $( echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]' ):$(date +%s) diff --git a/.github/workflows/reviewdog.yml b/.github/workflows/lint.yml similarity index 56% rename from .github/workflows/reviewdog.yml rename to .github/workflows/lint.yml index 84e523f..6c91c99 100644 --- a/.github/workflows/reviewdog.yml +++ b/.github/workflows/lint.yml @@ -1,4 +1,4 @@ -name: reviewdog +name: Lint on: push: @@ -7,8 +7,7 @@ on: pull_request: jobs: - shellcheck: - name: runner / shellcheck + Shellcheck: runs-on: ubuntu-latest steps: @@ -27,28 +26,7 @@ jobs: reporter: ${{ steps.reporter.outputs.value }} level: warning - hadolint: - name: runner / hadolint - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - - uses: haya14busa/action-cond@v1 - id: reporter - with: - cond: ${{ github.event_name == 'pull_request' }} - if_true: "github-pr-review" - if_false: "github-check" - - - uses: reviewdog/action-hadolint@v1 - with: - github_token: ${{ secrets.github_token }} - reporter: ${{ steps.reporter.outputs.value }} - level: warning - - misspell: - name: runner / misspell + Misspell: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 187dddb..fad9413 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,7 +10,7 @@ on: jobs: release: runs-on: ubuntu-latest - + steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/test.yml b/.github/workflows/tests.yml similarity index 64% rename from .github/workflows/test.yml rename to .github/workflows/tests.yml index 9151147..9864e56 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/tests.yml @@ -1,4 +1,4 @@ -name: Test +name: Tests on: push: @@ -6,9 +6,13 @@ on: - master pull_request: +defaults: + run: + shell: bash + jobs: test-check: - name: runner / tfsec (github-check) + name: tfsec (github-check) runs-on: ubuntu-latest steps: @@ -30,14 +34,14 @@ jobs: tfsec_return="${{ steps.test.outputs.tfsec-return-code }}" reviewdog_return="${{ steps.test.outputs.reviewdog-return-code }}" - if [ "$tfsec_return" -eq 1 ]; then + if [[ "$tfsec_return" -eq 1 ]]; then echo "tfsec correctly returned failure ${tfsec_return}" else echo "tfsec returned ${tfsec_return}, expected '1'. Failing..." exit 1 fi - if [ "$reviewdog_return" -eq 0 ]; then + if [[ "$reviewdog_return" -eq 0 ]]; then echo "reviewdog correctly returned success: ${reviewdog_return}" else echo "reviewdog returned ${reviewdog_return}, expected '0'. Failing..." @@ -46,7 +50,7 @@ jobs: test-pr-check: if: github.event_name == 'pull_request' - name: runner / tfsec (github-pr-check) + name: tfsec (github-pr-check) runs-on: ubuntu-latest steps: @@ -68,14 +72,14 @@ jobs: tfsec_return="${{ steps.test.outputs.tfsec-return-code }}" reviewdog_return="${{ steps.test.outputs.reviewdog-return-code }}" - if [ "$tfsec_return" -eq 1 ]; then + if [[ "$tfsec_return" -eq 1 ]]; then echo "tfsec correctly returned failure ${tfsec_return}" else echo "tfsec returned ${tfsec_return}, expected '1'. Failing..." exit 1 fi - if [ "$reviewdog_return" -eq 0 ]; then + if [[ "$reviewdog_return" -eq 0 ]]; then echo "reviewdog correctly returned success: ${reviewdog_return}" else echo "reviewdog returned ${reviewdog_return}, expected '0'. Failing..." @@ -84,7 +88,7 @@ jobs: test-pr-review: if: github.event_name == 'pull_request' - name: runner / tfsec (github-pr-review) + name: tfsec (github-pr-review) runs-on: ubuntu-latest steps: @@ -108,15 +112,55 @@ jobs: tfsec_return="${{ steps.test.outputs.tfsec-return-code }}" reviewdog_return="${{ steps.test.outputs.reviewdog-return-code }}" - if [ "$tfsec_return" -eq 1 ]; then + if [[ "$tfsec_return" -eq 1 ]]; then + echo "tfsec correctly returned failure ${tfsec_return}" + else + echo "tfsec returned ${tfsec_return}, expected '1'. Failing..." + exit 1 + fi + + if [[ "$reviewdog_return" -eq 0 ]]; then + echo "reviewdog correctly returned success: ${reviewdog_return}" + else + echo "reviewdog returned ${reviewdog_return}, expected '0'. Failing..." + exit 1 + fi + + test-operating-systems: + strategy: + matrix: + platform: [ubuntu-latest, macos-latest, windows-latest] + name: tfsec (${{ matrix.platform }}) + runs-on: ${{ matrix.platform }} + + steps: + - uses: actions/checkout@v2 + + - uses: ./ + continue-on-error: true + id: test + with: + github_token: ${{ secrets.github_token }} + reporter: github-check + level: info + working_directory: testdata + + # The check is expected to fail on the test data + - name: Check return codes + if: success() || failure () + run: | + tfsec_return="${{ steps.test.outputs.tfsec-return-code }}" + reviewdog_return="${{ steps.test.outputs.reviewdog-return-code }}" + + if [[ "$tfsec_return" -eq 1 ]]; then echo "tfsec correctly returned failure ${tfsec_return}" else echo "tfsec returned ${tfsec_return}, expected '1'. Failing..." exit 1 fi - if [ "$reviewdog_return" -eq 0 ]; then - echo "reviewdog correctly returned failure: ${reviewdog_return}" + if [[ "$reviewdog_return" -eq 0 ]]; then + echo "reviewdog correctly returned success: ${reviewdog_return}" else echo "reviewdog returned ${reviewdog_return}, expected '0'. Failing..." exit 1 diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index b0cbaab..0000000 --- a/Dockerfile +++ /dev/null @@ -1,18 +0,0 @@ -FROM alpine:3.13 - -ENV REVIEWDOG_VERSION=v0.11.0 - -# hadolint ignore=DL3018 -RUN apk --no-cache --update add bash git \ - && rm -rf /var/cache/apk/* - -SHELL ["/bin/bash", "-eo", "pipefail", "-c"] - -RUN wget -O - -q https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh | sh -s -- -b /usr/local/bin/ - -RUN wget -O - -q "$(wget -q https://api.github.com/repos/liamg/tfsec/releases/latest -O - | grep -o -E "https://.+?tfsec-linux-amd64")" > tfsec \ - && install tfsec /usr/local/bin/ - -COPY entrypoint.sh /entrypoint.sh - -ENTRYPOINT ["/entrypoint.sh"] diff --git a/README.md b/README.md index 3974e95..51597c2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # GitHub Action: Run tfsec with reviewdog -[![Test](https://github.com/reviewdog/action-tfsec/workflows/Test/badge.svg)](https://github.com/reviewdog/action-tfsec/actions?query=workflow%3ATest) -[![reviewdog](https://github.com/reviewdog/action-tfsec/workflows/reviewdog/badge.svg)](https://github.com/reviewdog/action-tfsec/actions?query=workflow%3Areviewdog) +[![Tests](https://github.com/reviewdog/action-tfsec/workflows/Tests/badge.svg)](https://github.com/reviewdog/action-tfsec/actions?query=workflow%3ATests) +[![Lint](https://github.com/reviewdog/action-tfsec/workflows/Lint/badge.svg)](https://github.com/reviewdog/action-tfsec/actions?query=workflow%Lint) [![depup](https://github.com/reviewdog/action-tfsec/workflows/depup/badge.svg)](https://github.com/reviewdog/action-tfsec/actions?query=workflow%3Adepup) [![release](https://github.com/reviewdog/action-tfsec/workflows/release/badge.svg)](https://github.com/reviewdog/action-tfsec/actions?query=workflow%3Arelease) [![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/reviewdog/action-tfsec?logo=github&sort=semver)](https://github.com/reviewdog/action-tfsec/releases) @@ -33,6 +33,11 @@ the Pull Request Conversation: **Required**. Must be in form of `github_token: ${{ secrets.github_token }}`. +### `working_directory` + +Optional. Directory to run the action on, from the repo root. +The default is `.` ( root of the repository). + ### `level` Optional. Report level for reviewdog [`info`,`warning`,`error`]. @@ -50,7 +55,7 @@ Optional. Filtering for the reviewdog command [`added`,`diff_context`,`file`,`no The default is `added`. -See [reviewdog doccumentation for filter mode](https://github.com/reviewdog/reviewdog/tree/master#filter-mode) for details. +See [reviewdog documentation for filter mode](https://github.com/reviewdog/reviewdog/tree/master#filter-mode) for details. ### `fail_on_error` @@ -58,14 +63,14 @@ Optional. Exit code for reviewdog when errors are found [`true`,`false`]. The default is `false`. -See [reviewdog doccumentation for exit codes](https://github.com/reviewdog/reviewdog/tree/master#exit-codes) for details. +See [reviewdog documentation for exit codes](https://github.com/reviewdog/reviewdog/tree/master#exit-codes) for details. -### `working_directory` +### `flags` -Optional. Directory to run the action on, from the repo root. -The default is `.` ( root of the repository). +Optional. Additional reviewdog flags. Useful for debugging errors, when it can be set to `-tee`. +The default is ``. -### `flags` +### `tfsec_flags` Optional. List of arguments to send to tfsec. For the output to be parsable by reviewdog [`--format=checkstyle` is enforced](./entrypoint.sh). @@ -89,21 +94,23 @@ on: [pull_request] jobs: tfsec: name: runner / tfsec - runs-on: ubuntu-latest + runs-on: ubuntu-latest # Windows and macOS are also supported steps: - name: Clone repo - uses: actions/checkout@master + uses: actions/checkout@v2 - - name: tfsec + - name: Run tfsec with reviewdog output on the PR uses: reviewdog/action-tfsec@master with: github_token: ${{ secrets.github_token }} - working_directory: "testdata" # Change working directory - reporter: github-pr-review # Change reporter - fail_on_error: "true" # Fail action if errors are found - filter_mode: "nofilter" # Check all files, not just the diff - flags: "" # Optional + working_directory: my_directory # Change working directory + level: info # Get more output from reviewdog + reporter: github-pr-review # Change reviewdog reporter + filter_mode: nofilter # Check all files, not just the diff + fail_on_error: true # Fail action if errors are found + flags: -tee # Add debug flag to reviewdog + tfsec_flags: "" # Optional ``` ## Development @@ -111,13 +118,14 @@ jobs: ### Release #### [haya14busa/action-bumpr](https://github.com/haya14busa/action-bumpr) + You can bump version on merging Pull Requests with specific labels (bump:major,bump:minor,bump:patch). Pushing tag manually by yourself also work. #### [haya14busa/action-update-semver](https://github.com/haya14busa/action-update-semver) This action updates major/minor release tags on a tag push. e.g. Update v1 and v1.2 tag when released v1.2.3. -ref: https://help.github.com/en/articles/about-actions#versioning-your-action +ref: ### Lint - reviewdog integration @@ -131,5 +139,6 @@ Supported linters: - [reviewdog/action-misspell](https://github.com/reviewdog/action-misspell) ### Dependencies Update Automation + This repository uses [haya14busa/action-depup](https://github.com/haya14busa/action-depup) to update reviewdog version. diff --git a/action.yml b/action.yml index a8d2b25..f60db59 100644 --- a/action.yml +++ b/action.yml @@ -6,45 +6,75 @@ inputs: github_token: description: 'GITHUB_TOKEN' required: true + working_directory: + description: | + Directory to run the action on, from the repo root. + Default is . ( root of the repository) + default: '.' + required: false level: description: 'Report level for reviewdog [info,warning,error]' default: 'error' + required: false reporter: description: | Reporter of reviewdog command [github-pr-check,github-pr-review]. Default is github-pr-check. default: 'github-pr-check' + required: false filter_mode: description: | Filtering for the reviewdog command [added,diff_context,file,nofilter]. Default is added. default: 'added' + required: false fail_on_error: description: | Exit code for reviewdog when errors are found [true,false] Default is `false`. default: 'false' - working_directory: - description: | - Directory to run the action on, from the repo root. - Default is . ( root of the repository) - default: '.' + required: false flags: + description: 'Additional reviewdog flags' + default: '' + required: false + tfsec_flags: description: | List of arguments to send to tfsec For the output to be parsable by reviewdog --format=checkstyle is enforced Default is blank. default: '' + required: false outputs: tfsec-return-code: description: 'tfsec command return code' + value: ${{ steps.tfsec.outputs.tfsec-return-code }} reviewdog-return-code: description: 'reviewdog command return code' + value: ${{ steps.tfsec.outputs.reviewdog-return-code }} runs: - using: 'docker' - image: 'Dockerfile' + using: 'composite' + steps: + - run: $GITHUB_ACTION_PATH/script.sh + id: tfsec + shell: bash + env: + # We may want to allow specifying reviewdog version as + # action's input, but let's start with hard coded latest stable version for reviewdog + REVIEWDOG_VERSION: v0.11.0 + # INPUT_ is not available in Composite run steps + # https://github.community/t/input-variable-name-is-not-available-in-composite-run-steps/127611 + INPUT_GITHUB_TOKEN: ${{ inputs.github_token }} + INPUT_WORKING_DIRECTORY: ${{ inputs.working_directory }} + INPUT_LEVEL: ${{ inputs.level }} + INPUT_REPORTER: ${{ inputs.reporter }} + INPUT_FILTER_MODE: ${{ inputs.filter_mode }} + INPUT_FAIL_ON_ERROR: ${{ inputs.fail_on_error }} + INPUT_FLAGS: ${{ inputs.flags }} + INPUT_TFSEC_FLAGS: ${{ inputs.tfsec_flags }} + branding: icon: 'edit' color: 'gray-dark' diff --git a/entrypoint.sh b/entrypoint.sh deleted file mode 100755 index 20d4ba8..0000000 --- a/entrypoint.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -if [ -n "${GITHUB_WORKSPACE}" ]; then - cd "${GITHUB_WORKSPACE}" || exit -fi - -export REVIEWDOG_GITHUB_API_TOKEN="${INPUT_GITHUB_TOKEN}" - -tfsec --format=checkstyle ${INPUT_FLAGS} "${INPUT_WORKING_DIRECTORY}" \ - | reviewdog -f=checkstyle -name="tfsec" -reporter="${INPUT_REPORTER}" -level="${INPUT_LEVEL}" -fail-on-error="${INPUT_FAIL_ON_ERROR}" -filter-mode="${INPUT_FILTER_MODE}" - -tfsec_return="${PIPESTATUS[0]}" reviewdog_return="${PIPESTATUS[1]}" exit_code=$? - -echo ::set-output name=tfsec-return-code::"${tfsec_return}" -echo ::set-output name=reviewdog-return-code::"${reviewdog_return}" - -exit $exit_code diff --git a/script.sh b/script.sh new file mode 100755 index 0000000..c23fa06 --- /dev/null +++ b/script.sh @@ -0,0 +1,76 @@ +#!/bin/bash + +# Fail fast on errors, unset variables, and failures in piped commands +set -Eeuo pipefail + +cd "${GITHUB_WORKSPACE}/${INPUT_WORKING_DIRECTORY}" || exit + +echo '::group::Preparing ...' + unameOS="$(uname -s)" + case "${unameOS}" in + Linux*) os=linux;; + Darwin*) os=darwin;; + CYGWIN*) os=windows;; + MINGW*) os=windows;; + MSYS_NT*) os=windows;; + *) echo "Unknown system: ${unameOS}" && exit 1 + esac + + unameArch="$(uname -m)" + case "${unameArch}" in + x86*) arch=amd64;; + *) echo "Unsupported architecture: ${unameArch}. Only AMD64 is supported by tfsec" && exit 1 + esac + + TEMP_PATH="$(mktemp -d)" + echo "Detected ${os} running on ${arch}, will install tools in ${TEMP_PATH}" + REVIEWDOG_PATH="${TEMP_PATH}/reviewdog" + TFSEC_PATH="${TEMP_PATH}/tfsec" +echo '::endgroup::' + +echo "::group::🐶 Installing reviewdog (${REVIEWDOG_VERSION}) ... https://github.com/reviewdog/reviewdog" + curl -sfL https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh | sh -s -- -b "${REVIEWDOG_PATH}" "${REVIEWDOG_VERSION}" 2>&1 +echo '::endgroup::' + +echo '::group:: Installing tfsec (latest) ... https://github.com/tfsec/tfsec' + test ! -d "${TFSEC_PATH}" && install -d "${TFSEC_PATH}" + + binary="tfsec" + url="https://github.com/tfsec/tfsec/releases/latest/download/tfsec-${os}-${arch}" + if [[ "${os}" = "windows" ]]; then + url+=".exe" + binary+=".exe" + fi + + curl --silent --show-error --fail \ + --location "${url}" \ + --output "${binary}" + install tfsec "${TFSEC_PATH}" +echo '::endgroup::' + +echo "::group:: Print tfsec details ..." + "${TFSEC_PATH}/tfsec" --version +echo '::endgroup::' + +echo '::group:: Running tfsec with reviewdog 🐶 ...' + export REVIEWDOG_GITHUB_API_TOKEN="${INPUT_GITHUB_TOKEN}" + + # Allow failures now, as reviewdog handles them + set +Eeuo pipefail + + # shellcheck disable=SC2086 + "${TFSEC_PATH}/tfsec" --format=checkstyle ${INPUT_TFSEC_FLAGS:-} . \ + | "${REVIEWDOG_PATH}/reviewdog" -f=checkstyle \ + -name="tfsec" \ + -reporter="${INPUT_REPORTER}" \ + -level="${INPUT_LEVEL}" \ + -fail-on-error="${INPUT_FAIL_ON_ERROR}" \ + -filter-mode="${INPUT_FILTER_MODE}" \ + ${INPUT_FLAGS} + + tfsec_return="${PIPESTATUS[0]}" reviewdog_return="${PIPESTATUS[1]}" exit_code=$? + echo "::set-output name=tfsec-return-code::${tfsec_return}" + echo "::set-output name=reviewdog-return-code::${reviewdog_return}" +echo '::endgroup::' + +exit "${exit_code}" diff --git a/testdata/example.tf b/testdata/example.tf index b1cef17..660b51b 100644 --- a/testdata/example.tf +++ b/testdata/example.tf @@ -1,19 +1,45 @@ -resource "aws_security_group_rule" "my-rule" { - type = "ingress" - cidr_blocks = ["0.0.0.0/0"] +resource "aws_vpc" "main" { + cidr_block = "10.0.0.0/16" } -resource "aws_alb_listener" "my-alb-listener"{ - port = "80" - protocol = "HTTP" +resource "aws_security_group" "example" { + name = "example" + description = "Very complex Security Group for testing" + vpc_id = aws_vpc.main.id + + tags = { + "Name" = "example" + } +} + +resource "aws_security_group_rule" "example_rule" { + security_group_id = aws_security_group.example.id + description = "Allow all traffic from Internet which will make tfsec throw an error" + + type = "ingress" + from_port = "0" + to_port = "65535" + protocol = "tcp" + + cidr_blocks = ["0.0.0.0/0"] + ipv6_cidr_blocks = ["::/0"] } -resource "aws_db_security_group" "my-group" { +resource "azurerm_resource_group" "example" { + name = "example-resources" + location = "West Europe" } -resource "azurerm_managed_disk" "source" { - encryption_settings { - enabled = false - } +resource "azurerm_managed_disk" "example" { + name = "acctestmd" + location = "West Europe" + resource_group_name = azurerm_resource_group.example.name + storage_account_type = "Standard_LRS" + create_option = "Empty" + disk_size_gb = "1" + + encryption_settings { + enabled = false + } } diff --git a/testdata/main.tf b/testdata/main.tf new file mode 100644 index 0000000..ae2eece --- /dev/null +++ b/testdata/main.tf @@ -0,0 +1,23 @@ +terraform { + required_version = "~> 0.15" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 3" + } + + azurerm = { + source = "hashicorp/azurerm" + version = "~> 2" + } + } +} + +provider "aws" { + region = "eu-west-1" +} + +provider "azurerm" { + features {} +}