# Copyright 2020 Marc-Antoine Ruel. All rights reserved.
# Use of this source code is governed under the Apache License, Version 2.0
# that can be found in the LICENSE file.

# References:
# https://github.com/actions/checkout
# https://github.com/actions/setup-go
# https://help.github.com/en/actions/configuring-and-managing-workflows/authenticating-with-the-github_token#using-the-github_token-in-a-workflow
# https://help.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions/
# https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions
# https://docs.github.com/en/rest/commits/comments#create-a-commit-comment
# https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#pull_request
# https://docs.github.com/en/actions/learn-github-actions/contexts

on: [push, pull_request]
name: Run tests
jobs:
    # Runs go test both with code coverage sent to codecov, race detector and
    # benchmarks. At the end do a quick check to ensure the tests to not leave
    # files in the tree.
  test:
    name: "test: go${{matrix.gover}}.x/${{matrix.os}}"
    runs-on: "${{matrix.os}}"
    continue-on-error: true
    defaults:
      run:
        shell: bash
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
        # Do not forget to bump every 6 months!
        gover: ["1.19"]
    env:
      PYTHONDONTWRITEBYTECODE: x
    steps:
    - name: Turn off git core.autocrlf
      if: matrix.os == 'windows-latest'
      run: git config --global core.autocrlf false
    - uses: actions/checkout@v3
      with:
        fetch-depth: 2
    - uses: actions/setup-go@v3
      with:
        go-version: "~${{matrix.gover}}.0"
        cache: true
    - name: 'go install necessary tools'
      if: always()
      run: |
        go install github.com/maruel/pat/cmd/ba@latest
    - name: 'Check: go test -cover'
      if: always()
      run: go test -timeout=120s -covermode=count -coverprofile coverage.txt -bench=. -benchtime=1x ./...
      # Don't send code coverage if anything failed to reduce spam.
    - uses: codecov/codecov-action@v2
    - name: 'Cleanup'
      if: always()
      run: rm coverage.txt
    - name: 'Check: go test -race'
      run: go test -timeout=120s -race -bench=. -benchtime=1x ./...
    - name: 'Check: benchmark 📈'
      run: ba -against HEAD~1
    - name: 'Check: go test -short (CGO_ENABLED=0)'
      env:
        CGO_ENABLED: 0
      run: go test -timeout=120s -short -bench=. -benchtime=1x ./...
    - name: 'Check: go test -short (32 bits)'
      if: matrix.os != 'macos-latest'
      env:
        GOARCH: 386
      run: go test -timeout=120s -short -bench=. -benchtime=1x ./...
    - name: "Check: tree is clean"
      if: always()
      run: |
        # Nothing should have changed in the tree up to that point and no
        # unsuspected file was created.
        TOUCHED=$(git status --porcelain --ignored)
        if ! test -z "$TOUCHED"; then
          echo "Oops, something touched these files, please cleanup:"
          echo "$TOUCHED"
          git diff
          false
        fi


    # Run linters. This workflow can be merged with the test_all one if desired
    # to cut on runtime, at the cost of latency. I dislike waiting for results
    # so I prefer to run them in parallel.
  lint:
    name: "lint: go${{matrix.gover}}.x/${{matrix.os}}"
    runs-on: "${{matrix.os}}"
    continue-on-error: true
    defaults:
      run:
        shell: bash
    strategy:
      fail-fast: false
      matrix:
        # You may want to run only on linux to save on cost. Projects with
        # OS-specific code benefits from explicitly linting on macOS and
        # Windows.
        os: [ubuntu-latest, macos-latest, windows-latest]
        # Do not forget to bump every 6 months!
        gover: ["1.19"]
    env:
      PYTHONDONTWRITEBYTECODE: x
    steps:
    - name: Turn off git core.autocrlf
      if: matrix.os == 'windows-latest'
      run: git config --global core.autocrlf false
    - uses: actions/checkout@v3
    - uses: actions/setup-go@v3
      with:
        go-version: "~${{matrix.gover}}.0"
        cache: true
    - name: "Debug"
      run: |
        echo HOME = $HOME
        echo GITHUB_WORKSPACE = $GITHUB_WORKSPACE
        echo PATH = $PATH
        echo ""
        echo $ ls -l $HOME/go/bin
        ls -la $HOME/go/bin
    - name: 'go install necessary tools'
      if: always()
      run: |
        go install github.com/gordonklaus/ineffassign@latest
        go install github.com/securego/gosec/v2/cmd/gosec@latest
        go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@latest
        go install honnef.co/go/tools/cmd/staticcheck@latest
    - name: 'go install necessary tools (ubuntu)'
      if: always() && matrix.os == 'ubuntu-latest'
      run: |
        go install github.com/client9/misspell/cmd/misspell@latest
        go install github.com/google/addlicense@latest
    - name: 'Check: go vet'
      if: always()
      run: go vet -unsafeptr=false ./...
    - name: 'Check: go vet shadow; shadowed variables'
      if: always()
      run: |
        SHADOW_TOOL="$(which shadow)"
        if [ -f "${SHADOW_TOOL}.exe" ]; then
          SHADOW_TOOL="${SHADOW_TOOL}.exe"
        fi
        go vet -vettool=$SHADOW_TOOL ./...
    - name: 'Check: inefficient variable assignment'
      if: always()
      run: ineffassign ./...
    - name: 'Check: staticcheck'
      if: always()
      run: staticcheck ./...
    - name: 'Check: gosec'
      if: always()
      run: gosec -fmt=golint -quiet ./...
      # The following checks are not dependent on the OS or go build tags. Only
      # run them on ubuntu-latest since it's the fastest one.
    - name: 'Check: no executable was committed (ubuntu)'
      if: always() && matrix.os == 'ubuntu-latest'
      run: |
        if find . -path '*.sh' -prune -o -path ./.git -prune -o -type f -executable -print | grep -e . ; then
          echo 'Do not commit executables beside shell scripts'
          false
        fi
    - name: 'Check: addlicense; all sources have a license header (ubuntu)'
      if: always() && matrix.os == 'ubuntu-latest'
      run: addlicense -check .
    - name: 'Check: gofmt; code is well formatted (ubuntu)'
      if: always() && matrix.os == 'ubuntu-latest'
      run: |
        FILES=$(gofmt -s -l .)
        if ! test -z "$FILES"; then
          echo 'Please run `gofmt -s -w` on the following files:' >> _gofmt.txt
          echo "" >> _gofmt.txt
          for FILE in ${FILES}; do
            echo "- ${FILE}" >> _gofmt.txt
          done
          cat _gofmt.txt
          echo "## ⚠ gofmt Failed" >> ../_comments.txt
          echo "" >> ../_comments.txt
          cat _gofmt.txt >> ../_comments.txt
          echo "" >> ../_comments.txt
          false
        fi
    - name: "Check: misspelling; code doesn't contain misspelling (ubuntu)"
      if: always() && matrix.os == 'ubuntu-latest'
      run: |
        ERR=$(misspell .)
        if ! test -z "$ERR"; then
          echo "$ERR"
          echo "## ⚠ misspell Failed" >> ../_comments.txt
          echo "" >> ../_comments.txt
          echo "$ERR" >> ../_comments.txt
          echo "" >> ../_comments.txt
          false
        fi
    - name: 'Send comments'
      if: failure()
      run: |
        if [ -f ../_comments.txt ]; then
          URL="${{github.event.issue.pull_request.url}}"
          if test -z "$URL"; then
            URL="${{github.api_url}}/repos/${{github.repository}}/commits/${{github.sha}}/comments"
          fi
          echo "Sending $(cat ../_comments.txt|wc -l) lines of comments to ${URL}"
          curl -sS --request POST \
            --header "Authorization: Bearer ${{secrets.GITHUB_TOKEN}}" \
            --header "Content-Type: application/json" \
            --data "$(cat ../_comments.txt | jq -R --slurp '{body: .}')" \
            "${URL}" > /dev/null
          rm ../_comments.txt
        fi
    - name: "Check: go generate doesn't modify files"
      if: always()
      run: |
        go generate ./...
        # Also test for untracked files. go generate should not generate ignored
        # files either.
        TOUCHED=$(git status --porcelain --ignored)
        if ! test -z "$TOUCHED"; then
          echo "go generate created these files, please fix:"
          echo "$TOUCHED"
          false
        fi
    - name: "Check: go mod tidy doesn't modify files"
      if: always()
      run: |
        go mod tidy
        TOUCHED=$(git status --porcelain --ignored)
        if ! test -z "$TOUCHED"; then
          echo "go mod tidy was not clean, please update:"
          git diff
          false
        fi


    # Ensure tests pass on oldest supported Go version.
    # Do while inside GOPATH, which tests different parts
    # of the code. This is panicparse specific and will be removed after 2022.
  old:
    name: "test: go${{matrix.gover}}/${{matrix.os}}"
    runs-on: "${{matrix.os}}"
    continue-on-error: true
    defaults:
      run:
        shell: bash
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest]
        gover: ['1.13.15', '1.14.7']
    env:
      PYTHONDONTWRITEBYTECODE: x
      GOPATH: ${{github.workspace}}
      GO111MODULE: off
    steps:
    - name: Turn off git core.autocrlf
      if: matrix.os == 'windows-latest'
      run: git config --global core.autocrlf false
    - uses: actions/checkout@v3
      with:
        path: src/github.com/maruel/panicparse
    - uses: actions/setup-go@v3
      with:
        go-version: "=${{matrix.gover}}"
    - name: 'Check: go get -d -t'
      working-directory: src/github.com/maruel/panicparse
      run: go get -d -t ./...
    - name: 'Check: go test'
      working-directory: src/github.com/maruel/panicparse
      run: go test -timeout=120s ./...


  codeql:
    name: "codeql: go${{matrix.gover}}.x/${{matrix.os}}"
    runs-on: "${{matrix.os}}"
    continue-on-error: true
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest]
        # Do not forget to bump every 6 months!
        gover: ["1.19"]
    permissions:
      security-events: write
    steps:
    - uses: actions/checkout@v3
    - uses: actions/setup-go@v3
      with:
        go-version: "~${{matrix.gover}}.0"
        cache: true
    - name: Initialize CodeQL
      uses: github/codeql-action/init@v2
      with:
        languages: go
    - name: Autobuild
      uses: github/codeql-action/autobuild@v2
    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@v2
