name: Lintcheck

on:
  pull_request:
    paths-ignore:
      - 'book/**'
      - 'util/**'
      - 'tests/**'
      - '*.md'

env:
  RUST_BACKTRACE: 1
  CARGO_INCREMENTAL: 0

concurrency:
  # For a given workflow, if we push to the same PR, cancel all previous builds on that PR.
  group: "${{ github.workflow }}-${{ github.event.pull_request.number}}"
  cancel-in-progress: true

jobs:
  # Runs lintcheck on the PR's target branch and stores the results as an artifact
  base:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout
      uses: actions/checkout@v6
      with:
        fetch-depth: 2
        # Unsetting this would make so that any malicious package could get our Github Token
        persist-credentials: false

    # HEAD is the generated merge commit `refs/pull/N/merge` between the PR and `master`, `HEAD^`
    # being the commit from `master` that is the base of the merge
    - name: Checkout base
      run: git checkout HEAD^

    # Use the lintcheck from the PR to generate the JSON in case the PR modifies lintcheck in some
    # way
    - name: Checkout current lintcheck
      run: |
        rm -rf lintcheck
        git checkout ${{ github.sha }} -- lintcheck

    - name: Cache lintcheck bin
      id: cache-lintcheck-bin
      uses: actions/cache@v5
      with:
        path: target/debug/lintcheck
        key: lintcheck-bin-${{ hashfiles('lintcheck/**') }}

    - name: Build lintcheck
      if: steps.cache-lintcheck-bin.outputs.cache-hit != 'true'
      run: cargo build --manifest-path=lintcheck/Cargo.toml

    - name: Create cache key
      id: key
      run: echo "key=lintcheck-base-${{ hashfiles('lintcheck/**') }}-$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"

    - name: Cache results JSON
      id: cache-json
      uses: actions/cache@v5
      with:
        path: lintcheck-logs/ci_crates_logs.json
        key: ${{ steps.key.outputs.key }}

    - name: Run lintcheck
      if: steps.cache-json.outputs.cache-hit != 'true'
      run: env CLIPPY_CONF_DIR="$PWD/lintcheck/ci-config" ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml

    - name: Upload base JSON
      uses: actions/upload-artifact@v7
      with:
        name: base
        path: lintcheck-logs/ci_crates_logs.json

  # Runs lintcheck on the PR and stores the results as an artifact
  head:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout
      uses: actions/checkout@v6
      with:
        # Unsetting this would make so that any malicious package could get our Github Token
        persist-credentials: false

    - name: Cache lintcheck bin
      id: cache-lintcheck-bin
      uses: actions/cache@v5
      with:
        path: target/debug/lintcheck
        key: lintcheck-bin-${{ hashfiles('lintcheck/**') }}

    - name: Build lintcheck
      if: steps.cache-lintcheck-bin.outputs.cache-hit != 'true'
      run: cargo build --manifest-path=lintcheck/Cargo.toml

    - name: Run lintcheck
      run: env CLIPPY_CONF_DIR="$PWD/lintcheck/ci-config" ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml

    - name: Upload head JSON
      uses: actions/upload-artifact@v7
      with:
        name: head
        path: lintcheck-logs/ci_crates_logs.json

  # Retrieves the head and base JSON results and prints the diff to the GH actions step summary
  diff:
    runs-on: ubuntu-latest

    needs: [base, head]

    steps:
    - name: Checkout
      uses: actions/checkout@v6
      with:
        # Unsetting this would make so that any malicious package could get our Github Token
        persist-credentials: false

    - name: Restore lintcheck bin
      uses: actions/cache/restore@v5
      with:
        path: target/debug/lintcheck
        key: lintcheck-bin-${{ hashfiles('lintcheck/**') }}
        fail-on-cache-miss: true

    - name: Download JSON
      uses: actions/download-artifact@v8

    - name: Store PR number
      run: echo ${{ github.event.pull_request.number }} > pr.txt

    - name: Diff results
      # GH's summery has a maximum size of 1MiB:
      # https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions#step-isolation-and-limits
      # We upload the full diff as an artifact in case it's truncated
      run: |
        ./target/debug/lintcheck diff {base,head}/ci_crates_logs.json --truncate | head -c 1M > $GITHUB_STEP_SUMMARY
        ./target/debug/lintcheck diff {base,head}/ci_crates_logs.json --write-summary summary.json > full_diff.md

    - name: Upload full diff
      uses: actions/upload-artifact@v7
      with:
        name: full_diff
        path: full_diff.md

    - name: Upload summary
      uses: actions/upload-artifact@v7
      with:
        name: summary
        path: |
          summary.json
          pr.txt
