Skip to content
Latchkey

Run tests workflow (maruel/panicparse)

The Run tests workflow from maruel/panicparse, explained and optimized by Latchkey.

C

CI health: C - fair

Run this on Latchkey for self-healing, caching, and up to 58% lower cost.

Grade your own workflow free or run it on Latchkey →
Source: maruel/panicparse.github/workflows/test.ymlLicense Apache-2.0View source

What it does

This is the Run tests workflow from the maruel/panicparse repository, a real project running GitHub Actions. It is shown here with attribution under its Apache-2.0 license.

Below, Latchkey shows a faster, safer version produced by its optimization engine.

The workflow

workflow (.yml)
# 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.

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.24"]
    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@v4
      with:
        fetch-depth: 2
    - uses: actions/setup-go@v5
      with:
        go-version: "~${{matrix.gover}}.0"
    - 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@v4
      with:
        token: ${{secrets.CODECOV_TOKEN}}
    - 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.23"]
    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@v4
    - uses: actions/setup-go@v5
      with:
        go-version: "~${{matrix.gover}}.0"
    - name: 'go install necessary tools'
      if: always()
      run: |
        go install github.com/gordonklaus/ineffassign@latest
        go install github.com/securego/gosec/v2/cmd/gosec@v2.18.2
        go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@v0.24.0
        go install honnef.co/go/tools/cmd/staticcheck@v0.5.1
    - 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]
        # TODO: switch to an older Go version once tests are made compatible
        gover: ['1.23']
    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@v4
    - uses: actions/setup-go@v5
      with:
        go-version: "=${{matrix.gover}}"
    - name: 'Check: go test'
      run: go test -timeout=120s -bench=. -benchtime=1x ./...


  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.23"]
    permissions:
      security-events: write
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-go@v5
      with:
        go-version: "~${{matrix.gover}}.0"
    - name: Initialize CodeQL
      uses: github/codeql-action/init@v3
      with:
        languages: go
    - name: Autobuild
      uses: github/codeql-action/autobuild@v3
    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@v3

The same workflow, on Latchkey

Removes redundant runs and caps runaway jobs. Added and changed lines are highlighted.

# 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.
 
on: [push, pull_request]
name: Run tests
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
 
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:
    timeout-minutes: 30
    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.24"]
    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@v4
      with:
        fetch-depth: 2
    - uses: actions/setup-go@v5
      with:
        go-version: "~${{matrix.gover}}.0"
    - 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@v4
      with:
        token: ${{secrets.CODECOV_TOKEN}}
    - 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:
    timeout-minutes: 30
    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.23"]
    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@v4
    - uses: actions/setup-go@v5
      with:
        go-version: "~${{matrix.gover}}.0"
    - name: 'go install necessary tools'
      if: always()
      run: |
        go install github.com/gordonklaus/ineffassign@latest
        go install github.com/securego/gosec/v2/cmd/gosec@v2.18.2
        go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@v0.24.0
        go install honnef.co/go/tools/cmd/staticcheck@v0.5.1
    - 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:
    timeout-minutes: 30
    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]
        # TODO: switch to an older Go version once tests are made compatible
        gover: ['1.23']
    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@v4
    - uses: actions/setup-go@v5
      with:
        go-version: "=${{matrix.gover}}"
    - name: 'Check: go test'
      run: go test -timeout=120s -bench=. -benchtime=1x ./...
 
 
  codeql:
    timeout-minutes: 30
    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.23"]
    permissions:
      security-events: write
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-go@v5
      with:
        go-version: "~${{matrix.gover}}.0"
    - name: Initialize CodeQL
      uses: github/codeql-action/init@v3
      with:
        languages: go
    - name: Autobuild
      uses: github/codeql-action/autobuild@v3
    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@v3
 

What changed

1 third-party action is referenced by a movable tag. Pin it to the commit SHA (Latchkey resolves and applies this automatically) so a repointed tag cannot change what runs.

What Latchkey heals here

This workflow has steps that commonly fail on transient issues (network, registries, flaky browsers). On Latchkey managed runners they are detected, retried, and self-healed instead of failing your build:

This workflow runs 4 jobs (8 with the matrix expanded) per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.

Actions used in this workflow