# Copyright 2020 The Knative Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# This file is automagically synced here from github.com/knative-sandbox/.github
# repo by knobots: https://github.com/knative-sandbox/knobots and will be overwritten.

name: Code Style

on:
  pull_request:
    branches: [ 'master', 'release-*' ]

jobs:

  autoformat:
    name: Auto-format and Check
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false # Keep running if one leg fails.
      matrix:
        tool:
          - goimports
          - gofmt

        include:
        - tool: gofmt
          options: -s
        - tool: goimports
          importpath: golang.org/x/tools/cmd/goimports

    steps:
      - name: Set up Go 1.15.x
        uses: actions/setup-go@v2
        with:
          go-version: 1.15.x
        id: go

      - name: Check out code
        uses: actions/checkout@v2

      - name: Install Dependencies
        if: ${{ matrix.importpath != '' }}
        run: |
          cd $(mktemp -d)
          GO111MODULE=on go get ${{ matrix.importpath }}

      - name: ${{ matrix.tool }} ${{ matrix.options }}
        shell: bash
        run: |
          ${{ matrix.tool }} ${{ matrix.options }} -w $(find . -path './vendor' -prune -o -path './third_party' -prune -o -name '*.pb.go' -prune -o -type f -name '*.go' -print)

      - name: Verify ${{ matrix.tool }}
        shell: bash
        run: |
          # From: https://backreference.org/2009/12/23/how-to-match-newlines-in-sed/
          # This is to leverage this workaround:
          # https://github.com/actions/toolkit/issues/193#issuecomment-605394935
          function urlencode() {
            sed ':begin;$!N;s/\n/%0A/;tbegin'
          }
          if [[ $(git diff-index --name-only HEAD --) ]]; then
              for x in $(git diff-index --name-only HEAD --); do
                echo "::error file=$x::Please run ${{ matrix.tool }} ${{ matrix.options }}.%0A$(git diff $x | urlencode)"
              done
              echo "${{ github.repository }} is out of style. Please run ${{ matrix.tool }} ${{ matrix.options }}."
              exit 1
          fi
          echo "${{ github.repository }} is formatted correctly."

  lint:
    name: Lint
    runs-on: ubuntu-latest

    steps:
      - name: Set up Go 1.15.x
        uses: actions/setup-go@v2
        with:
          go-version: 1.15.x
        id: go

      - name: Check out code
        uses: actions/checkout@v2

      - name: Install Tools
        env:
          WOKE_VERSION: v0.1.15
        run: |
          TEMP_PATH="$(mktemp -d)"
          cd $TEMP_PATH

          echo '::group::🐶 Installing reviewdog ... https://github.com/reviewdog/reviewdog'
          curl -sfL https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh | sh -s -- -b "${TEMP_PATH}" 2>&1
          echo '::endgroup::'

          echo '::group:: Installing misspell ... https://github.com/client9/misspell'
          go get github.com/client9/misspell/cmd/misspell
          echo '::endgroup::'

          echo '::group:: Installing woke ... https://github.com/get-woke/woke'
          curl -sfL https://raw.githubusercontent.com/get-woke/woke/main/install.sh | sh -s -- -b "${TEMP_PATH}" "${WOKE_VERSION}" 2>&1
          echo '::endgroup::'

          echo "${TEMP_PATH}" >> $GITHUB_PATH

      - id: golangci_configuration
        uses: andstor/file-existence-action@v1
        with:
          files: .golangci.yaml
      - name: Go Lint
        if: steps.golangci_configuration.outputs.files_exists == 'true'
        uses: golangci/golangci-lint-action@v2
        with:
          version: v1.32

      - name: misspell
        shell: bash
        if: ${{ always() }}
        env:
          REVIEWDOG_GITHUB_API_TOKEN: ${{ github.token }}
        run: |
          set -e
          cd "${GITHUB_WORKSPACE}" || exit 1

          echo '::group:: Running github.com/client9/misspell with reviewdog 🐶 ...'
          # Don't fail because of misspell
          set +o pipefail
          find . -type f -not -path './vendor/*' -not -path './third_party/*' -not -path './.git/*' |
          xargs misspell -error |
          reviewdog -efm="%f:%l:%c: %m" \
                -name="github.com/client9/misspell" \
                -reporter="github-pr-check" \
                -filter-mode="added" \
                -fail-on-error="true" \
                -level="error"

          echo '::endgroup::'

      - name: trailing whitespace
        shell: bash
        if: ${{ always() }}
        env:
          REVIEWDOG_GITHUB_API_TOKEN: ${{ github.token }}
        run: |
          set -e
          cd "${GITHUB_WORKSPACE}" || exit 1

          echo '::group:: Flagging trailing whitespace with reviewdog 🐶 ...'
          # Don't fail because of grep
          set +o pipefail
          find . -type f -not -path './vendor/*' -not -path './third_party/*' -not -path './.git/*' |
          xargs grep -nE " +$" |
          reviewdog -efm="%f:%l:%m" \
                -name="trailing whitespace" \
                -reporter="github-pr-check" \
                -filter-mode="added" \
                -fail-on-error="true" \
                -level="error"

          echo '::endgroup::'

      - name: EOF newline
        shell: bash
        if: ${{ always() }}
        env:
          REVIEWDOG_GITHUB_API_TOKEN: ${{ github.token }}
        run: |
          set -e
          cd "${GITHUB_WORKSPACE}" || exit 1

          echo '::group:: Flagging missing EOF newlines with reviewdog 🐶 ...'
          # Don't fail because of misspell
          set +o pipefail
          # Lint exclude rule:
          #  - nothing in vendor/
          #  - nothing in third_party
          #  - nothing in .git/
          #  - no *.ai (Adobe Illustrator) files.
          for x in $(find . -type f -not -name '*.ai' -not -path './vendor/*' -not -path './third_party/*' -not -path './.git/*'); do
            # Based on https://stackoverflow.com/questions/34943632/linux-check-if-there-is-an-empty-line-at-the-end-of-a-file
            if [[ -f $x && ! ( -s "$x" && -z "$(tail -c 1 $x)" ) ]]; then
              # We add 1 to `wc -l` here because of this limitation (from the man page):
              # Characters beyond the final <newline> character will not be included in the line count.
              echo $x:$((1 + $(wc -l $x | tr -s ' ' | cut -d' ' -f 1))): Missing newline
            fi
          done |
          reviewdog -efm="%f:%l: %m" \
                -name="EOF Newline" \
                -reporter="github-pr-check" \
                -filter-mode="added" \
                -fail-on-error="true" \
                -level="error"

          echo '::endgroup::'

      - name: Redundant Format
        shell: bash
        if: ${{ always() }}
        env:
          REVIEWDOG_GITHUB_API_TOKEN: ${{ github.token }}
        run: |
          set -e
          cd "${GITHUB_WORKSPACE}" || exit 1

          echo '::group:: Flagging possibly unnecessary format calls for trailing argument with reviewdog 🐶 ...'
          # Don't fail because of misspell
          set +o pipefail
          # For all look for formatting calls and extract the ones that
          # have only a single formatting argument and that argument is in the last position.
          # But exclude the `fmt.Errorf` calls since there's no `fmt.Error` and `errors.New` is not
          # as versatile.
          # Also ignore `%T` — to print the type and `%q` to quote the final string.
          fn=$(find . -path './vendor' -prune -o -path './third_party' -prune -o -name '*.pb.go' -prune -o -type f -name '*.go' -print)
          grep -nE '[^.]+\.(Fatalf|Errorf|Warnf|Infof|Debugf|Logf|Sprintf|Fprintf|Printf)[^\%]+%[^Tqx]",[^,]+' $fn | grep -v "fmt.Errorf" |
            while read -r ent ; do
              file=$(echo $ent | cut -d':' -f 1);
              line=$(echo $ent | cut -d':' -f 2);
              ch=$(echo $ent | cut -d':' -f3-);
              err="Unknown printer tool, please file an issue in knative-sandbox/.github and assign to @vagababov: $ch"
              if echo $ch | grep --quiet -E "^t.(Errorf|Fatalf|Logf)" ; then
                err=$(echo $ch | sed  -E 's/([^.fm]+t\.)(Fatal|Error|Log)f([^\%]+)( %[^Tq]",)([^,]+)/\1\2\3",\5/')
                # Not a test. Here we deal with various loggers and fmt helpers.
              elif echo $ch | grep --quiet "log" ; then
                # Capture (x)?log(er)?.
                err=$(echo $ch | sed -E 's/(.*log.*\.)(Print|Fatal|Error|Info|Warn)f([^\%]+)(%[^Tq]",)([^,]+)/\1\2\3",\5/')
              elif echo $ch | grep --quiet -E "fmt\.Sprintf" ; then
                # Always space after sprintf
                err=$(echo $ch | sed -E 's/(fmt\.)(Sprint)f([^%]+) (%s",)([^,]+)/\1\2\3 ",\5/')
              elif echo $ch | grep --quiet -E "fmt\." ; then  # all other fmt. printers.
                err=$(echo $ch | sed -E 's/(fmt\.)(Print|Fprint)f([^%]+) (%[^sTxq]",)([^,]+)/\1\2\3",\5/')
              fi
              echo "$file:$line: Please consider avoiding tail format like this:%0A$err"
          done |
          reviewdog -efm="%f:%l: %m" \
                -name="Redundant Format" \
                -reporter="github-pr-check" \
                -filter-mode="added" \
                -fail-on-error="true" \
                -level="error"
          echo '::endgroup::'

      # This is mostly copied from https://github.com/get-woke/woke-action-reviewdog/blob/main/entrypoint.sh
      # since their action is not yet released under a stable version.
      - name: Language
        if: ${{ always() && github.event_name == 'pull_request' }}
        shell: bash
        env:
          REVIEWDOG_GITHUB_API_TOKEN: ${{ github.token }}
        run: |
          set -e
          cd "${GITHUB_WORKSPACE}" || exit 1

          # Create a minimal .wokeignore if none already exist.
          if [ ! -f .wokeignore ]; then
            cat > .wokeignore <<EOF
            vendor/*
            third_party/*
          EOF
          fi

          echo '::group:: Running woke with reviewdog 🐶 ...'
          woke --output simple \
            | reviewdog -efm="%f:%l:%c: %m" \
                -name="woke" \
                -reporter="github-pr-check" \
                -filter-mode="added" \
                -fail-on-error="true" \
                -level="error"
          echo '::endgroup::'
