diff --git a/.github/workflows/lint-pr.yml b/.github/workflows/lint-pr.yml new file mode 100644 index 00000000..12d73007 --- /dev/null +++ b/.github/workflows/lint-pr.yml @@ -0,0 +1,101 @@ +name: lint-pr + +# Additive PR-only layer on top of the full-scan lint gate. +# +# Master's `lint.yml` runs `golangci/golangci-lint-action` on every PR as a +# hard gate over the entire module. That keeps the bar high but the failure +# surface is a single check-run with all findings dumped in the job log, +# which is awkward when only a handful of lines actually changed. +# +# This workflow wraps the *same* linter version + config with reviewdog and +# `filter_mode: added`, so: +# - findings inside the PR diff appear as inline review comments on the +# exact line they reference (much easier to action than a log scrape), +# - pre-existing baseline noise is filtered out entirely (no duplicate +# reporting with the full-scan job), +# - the full-scan job in `lint.yml` remains the authoritative correctness +# gate, so this workflow is intentionally NON-blocking (`fail_level: +# warning` would still annotate without failing; we keep `fail_level: +# error` so a *new* hard-error inside the diff also surfaces here as a +# red check, but the master `lint.yml` is the merge gate). +# +on: + pull_request: + paths-ignore: + - "**.md" + - "docs/**" + - ".gitignore" + +permissions: + contents: read + pull-requests: write # reviewdog needs this to post inline review comments (same-repo PRs only) + checks: write # for the check-run summary + +jobs: + golangci-lint-diff: + name: golangci-lint (diff-only, inline) + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout code + uses: actions/checkout@v6.0.1 + with: + # reviewdog needs the merge-base to compute the PR diff. + fetch-depth: 0 + + - name: Set up Go + uses: ./.github/actions/setup-go + + - name: Install dependencies + run: go mod download + + # Same-repo PRs: post findings as inline review comments. The default + # GITHUB_TOKEN has the `pull-requests: write` scope declared above, + # so reviewdog can call the review API. Findings are filtered to lines + # the PR actually changed (`filter_mode: added`). + # + # We deliberately do NOT set `level: warning` here: combined with + # `fail_level: error` it would downgrade every result to warning and + # silently neuter the gate. Leaving `level` unset preserves each + # finding's native severity, so error-level diagnostics (e.g. govet, + # staticcheck SA*) trip `fail_level: error` as intended. + - name: golangci-lint via reviewdog (same-repo PR — inline comments) + if: github.event.pull_request.head.repo.full_name == github.repository + # Pin to v2.10.0 (>=v2.8.0 required for golangci-lint v2 support; + # earlier reviewdog releases on the @v2 major tag still default to + # downloading golangci-lint v1.x even when a v2.x version is + # requested via the input). + uses: reviewdog/action-golangci-lint@v2.10.0 + with: + go_version_file: go.mod + # Match the exact version used by master's full-scan lint gate. + golangci_lint_version: v2.11.3 + golangci_lint_flags: "--config=.golangci.yml --timeout=5m" + workdir: . + # Only annotate lines actually changed by the PR. + filter_mode: added + reporter: github-pr-review + fail_level: error + env: + REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # Fork PRs: GITHUB_TOKEN is strictly read-only regardless of the + # workflow-level permissions block, so neither `github-pr-review` + # (needs pull-requests: write) nor `github-pr-check` (needs + # checks: write) can post results. The `local` reporter writes + # findings to the job log only. `fail_level: error` still makes new + # error-level diagnostics fail the job, but the master `lint.yml` + # full-scan remains the actual merge gate for fork PRs. + - name: golangci-lint via reviewdog (fork PR — log-only) + if: github.event.pull_request.head.repo.full_name != github.repository + uses: reviewdog/action-golangci-lint@v2.10.0 + with: + go_version_file: go.mod + golangci_lint_version: v2.11.3 + golangci_lint_flags: "--config=.golangci.yml --timeout=5m" + workdir: . + filter_mode: added + reporter: local + fail_level: error + env: + REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}