From 80330f363c57dd7b1e5fb16fd972bfe3901631a4 Mon Sep 17 00:00:00 2001 From: jddunn Date: Sat, 17 Oct 2026 08:42:18 -0700 Subject: [PATCH 1/3] ci: post ESLint's findings on the lines a pull request adds as review comments A pull request from a branch of this repository that changes src/, the ESLint config, package.json or this workflow runs ESLint with the lint script's pattern on its head, and each finding on an added line becomes a review comment. The job never fails and is not a required check; the build job's lint step is unchanged. Forks and Dependabot are skipped, since their token cannot post a review. CONTRIBUTING.md describes it. --- .github/workflows/review-comments.yml | 56 +++++++++++++++++++++++++++ CONTRIBUTING.md | 2 + 2 files changed, 58 insertions(+) create mode 100644 .github/workflows/review-comments.yml diff --git a/.github/workflows/review-comments.yml b/.github/workflows/review-comments.yml new file mode 100644 index 0000000..f79d30a --- /dev/null +++ b/.github/workflows/review-comments.yml @@ -0,0 +1,56 @@ +name: Review comments + +# Posts ESLint's findings on the lines a pull request adds as review comments, +# through reviewdog. It never fails: the build job's lint step in ci.yml is +# unchanged, and this workflow is not a required check. The path filter skips +# pull requests that change nothing ESLint reads, so it has to stay optional. + +on: + pull_request: + types: [opened, synchronize, reopened] + paths: + - 'src/**' + - 'eslint.config.js' + - 'package.json' + - '.github/workflows/review-comments.yml' + +permissions: + contents: read + pull-requests: write + checks: write + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + eslint: + # A fork's token, and Dependabot's, can read the repository but cannot post a review. + if: github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]' + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + # The pull request's head, so a finding's line number is its line in the diff. + - uses: actions/checkout@v5 + with: + ref: ${{ github.event.pull_request.head.sha }} + persist-credentials: false + - uses: actions/setup-node@v5 + with: + node-version: '22' + + # ESLint and its plugins are plain JavaScript: no build, no native modules. + - name: Install dependencies + run: npm install --ignore-scripts --no-audit --no-fund + + - name: Post ESLint findings on added lines + uses: reviewdog/action-eslint@3ee2a450da17e060b9dfa4bffd53976906c6b2b0 # v1.36.1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + reporter: github-pr-review + filter_mode: added + # The action deprecates fail_on_error; fail_level none is its replacement and never fails. + fail_level: none + level: warning + # The pattern of npm run lint. The inner quotes leave the glob for ESLint to expand. + eslint_flags: '"src/**/*.{ts,tsx}"' diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 32466bc..fa4b9bb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -38,6 +38,8 @@ By participating in this project, you agree to abide by our code of conduct: be 8. Push to your fork 9. Open a Pull Request +When a pull request from a branch of this repository (not a fork) changes a file under `src/`, the Review comments workflow posts ESLint's findings on the lines it adds as review comments. The workflow never fails the pull request. + ## Development Setup ### Prerequisites From 6eec3660a7886ac9e2832bbce050074ec24c0250 Mon Sep 17 00:00:00 2001 From: jddunn Date: Sat, 17 Oct 2026 09:01:44 -0700 Subject: [PATCH 2/3] ci: drop the unused checks scope from the review comments workflow and pin its actions by commit reviewdog's github-pr-review reporter writes through the pull request API alone (reviews and review comments), so checks: write was a scope it never used. actions/checkout and actions/setup-node are pinned to the commits their v5 tags point at today (v5.1.0 and v5.0.0), the versions the run already used. --- .github/workflows/review-comments.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/review-comments.yml b/.github/workflows/review-comments.yml index f79d30a..62a8d92 100644 --- a/.github/workflows/review-comments.yml +++ b/.github/workflows/review-comments.yml @@ -14,10 +14,11 @@ on: - 'package.json' - '.github/workflows/review-comments.yml' +# reviewdog's github-pr-review reporter creates, lists and deletes review +# comments; pull-requests is the one scope it writes. permissions: contents: read pull-requests: write - checks: write concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number }} @@ -26,16 +27,18 @@ concurrency: jobs: eslint: # A fork's token, and Dependabot's, can read the repository but cannot post a review. + # The head's packages and ESLint config run with this token; that gives a branch's + # author nothing new, since they can already change any workflow on the branch. if: github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]' runs-on: ubuntu-latest timeout-minutes: 15 steps: # The pull request's head, so a finding's line number is its line in the diff. - - uses: actions/checkout@v5 + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false - - uses: actions/setup-node@v5 + - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version: '22' From e37eb06875a6799ff2b5c8c33c24e320a10d26e3 Mon Sep 17 00:00:00 2001 From: jddunn Date: Sat, 17 Oct 2026 13:49:05 -0700 Subject: [PATCH 3/3] ci: run the review comments workflow on every pull request and on a change of base reviewdog deletes its earlier comments whose finding is gone, but only in a run; with the path filter, a push that dropped a pull request's last change under src/ started none, so the stale comment stayed. The workflow now runs on every pull request from a branch of this repository. edited is added for a change of base branch, so the comments follow the new diff. A title or body edit skips the job, and its run takes a concurrency group of its own, so it cannot cancel a lint run in progress. The job's comment gave the wrong reason for skipping Dependabot: the permissions key raises a Dependabot run's token, so it could post. Dependabot changes manifests and never a line under src/, and skipping its runs keeps a newly bumped ESLint package from running with this token. --- .github/workflows/review-comments.yml | 36 +++++++++++++++++---------- CONTRIBUTING.md | 2 +- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/.github/workflows/review-comments.yml b/.github/workflows/review-comments.yml index 62a8d92..210aae4 100644 --- a/.github/workflows/review-comments.yml +++ b/.github/workflows/review-comments.yml @@ -2,17 +2,17 @@ name: Review comments # Posts ESLint's findings on the lines a pull request adds as review comments, # through reviewdog. It never fails: the build job's lint step in ci.yml is -# unchanged, and this workflow is not a required check. The path filter skips -# pull requests that change nothing ESLint reads, so it has to stay optional. +# unchanged, and this workflow is not a required check. +# +# There is no path filter. reviewdog deletes its earlier comments whose finding +# is gone, but only in a run, so a push that drops a pull request's last change +# under src/ still needs one. on: pull_request: - types: [opened, synchronize, reopened] - paths: - - 'src/**' - - 'eslint.config.js' - - 'package.json' - - '.github/workflows/review-comments.yml' + # edited runs the job only when the base branch changed (see the job's if), + # so the comments follow the new diff; a title or body edit runs nothing. + types: [opened, synchronize, reopened, edited] # reviewdog's github-pr-review reporter creates, lists and deletes review # comments; pull-requests is the one scope it writes. @@ -20,16 +20,26 @@ permissions: contents: read pull-requests: write +# A title or body edit takes a group of its own (its run id), so it cancels no +# lint run; any other run replaces the one before it on the same pull request. concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number }} + group: ${{ github.workflow }}-${{ github.event.pull_request.number }}-${{ github.event.action == 'edited' && !github.event.changes.base && github.run_id || 'eslint' }} cancel-in-progress: true jobs: eslint: - # A fork's token, and Dependabot's, can read the repository but cannot post a review. - # The head's packages and ESLint config run with this token; that gives a branch's - # author nothing new, since they can already change any workflow on the branch. - if: github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]' + # Forks are skipped: their token is read-only, so reviewdog could not post. + # Runs that Dependabot triggers are skipped: Dependabot changes manifests, + # never a line under src/, and skipping them keeps a newly bumped ESLint + # package from running with this token. A maintainer's push to a Dependabot + # branch still runs. + # A branch of this repository runs its own packages and ESLint config with + # this token; that gives its author nothing new, since they can already + # change any workflow on the branch. + if: >- + github.event.pull_request.head.repo.full_name == github.repository && + github.actor != 'dependabot[bot]' && + (github.event.action != 'edited' || github.event.changes.base) runs-on: ubuntu-latest timeout-minutes: 15 steps: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fa4b9bb..e167f16 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -38,7 +38,7 @@ By participating in this project, you agree to abide by our code of conduct: be 8. Push to your fork 9. Open a Pull Request -When a pull request from a branch of this repository (not a fork) changes a file under `src/`, the Review comments workflow posts ESLint's findings on the lines it adds as review comments. The workflow never fails the pull request. +On a pull request from a branch of this repository (not a fork), the Review comments workflow posts ESLint's findings on the lines the pull request adds under `src/` as review comments. Each run also deletes its earlier comments whose finding is gone, unless someone has replied to the comment. The workflow never fails the pull request. ## Development Setup