Skip to content
Open
143 changes: 143 additions & 0 deletions .github/workflows/pr-reviewdog.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
name: PR Reviewdog

on:
workflow_run:
workflows:
- "Test"
types:
- completed

permissions:
# Download artifact from lint workflow.
actions: read
# Post inline review comments via reviewdog.
pull-requests: write
# Report commit status.
statuses: write

jobs:
reviewdog:
runs-on: ubuntu-latest
env:
STATUS_PATH: repos/${{ github.repository }}/statuses/${{ github.event.workflow_run.head_sha }}
STATUS_CONTEXT: ${{ github.workflow }}
STATUS_TARGET: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
steps:
- name: Mark status as pending
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh api "$STATUS_PATH" \
-f state=pending \
-f context="$STATUS_CONTEXT" \
-f description='Running' \
-f target_url="$STATUS_TARGET"

- name: Identify PR
id: identify-pr
env:
BASE_REPO: ${{ github.repository }}
GITHUB_TOKEN: ${{ github.token }}
HEAD_REPO: ${{ github.event.workflow_run.head_repository.full_name }}
HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
run: |
PR_NUMBER=$(gh api "repos/$HEAD_REPO/commits/$HEAD_SHA/pulls" \
--jq ".[] | select(.base.repo.full_name == \"$BASE_REPO\") | .number")
echo "number=$PR_NUMBER" >> $GITHUB_OUTPUT

- name: Download artifact
if: steps.identify-pr.outputs.number
continue-on-error: true
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: lint-results
path: lint-results
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}

- name: Check for artifact
Comment thread
caugner marked this conversation as resolved.
Dismissed
id: check
if: steps.identify-pr.outputs.number && hashFiles('lint-results/') != ''
run: echo "HAS_ARTIFACT=true" >> "$GITHUB_OUTPUT"

- name: Checkout
if: steps.identify-pr.outputs.number
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
path: browser-compat-data
ref: main
persist-credentials: false

- name: Setup reviewdog
if: steps.check.outputs.HAS_ARTIFACT == 'true'
uses: reviewdog/action-setup@d8a7baabd7f3e8544ee4dbde3ee41d0011c3a93f # v1.5.0
with:
reviewdog_version: v0.21.0

- name: Add PR review with suggested changes using `git diff` output
if: steps.check.outputs.HAS_ARTIFACT == 'true' && hashFiles('lint-results/lint.diff') != ''
working-directory: browser-compat-data
env:
CI_PULL_REQUEST: ${{ steps.identify-pr.outputs.number }}
CI_COMMIT: ${{ github.event.workflow_run.head_sha }}
CI_REPO_OWNER: ${{ github.repository_owner }}
CI_REPO_NAME: ${{ github.event.repository.name }}
CI_BRANCH: ${{ github.event.workflow_run.head_branch }}
REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
env -u GITHUB_ACTIONS reviewdog \
-guess \
-name="bcd-linter" \
-f=diff \
-f.diff.strip=1 \
-filter-mode=diff_context \
-reporter=github-pr-review < ../lint-results/lint.diff

- name: Post or update PR comment with context
if: steps.check.outputs.HAS_ARTIFACT == 'true' && hashFiles('lint-results/lint.diff') != ''
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
PR_NUMBER: ${{ steps.identify-pr.outputs.number }}
TEST_RUN_ID: ${{ github.event.workflow_run.id }}
TEST_RUN_URL: ${{ github.event.workflow_run.html_url }}
run: |
LINT_JOB_URL=$(gh api --paginate "repos/$REPO/actions/runs/$TEST_RUN_ID/jobs" \
--jq '.jobs[] | select(.name == "lint") | .html_url' | head -1)
LINT_JOB_URL=${LINT_JOB_URL:-$TEST_RUN_URL}

BODY="<!-- bcd-reviewdog -->
The [lint check]($LINT_JOB_URL) found auto-fixable issues. Apply suggested changes (attributed to \`bcd-linter\`), or to fix all at once, run \`npm run lint:fix\` locally.

See also: [Automated lint suggestions on pull requests](https://github.com/$REPO/blob/main/docs/testing.md#automated-lint-suggestions-on-pull-requests)"

COMMENT_ID=$(gh api --paginate "repos/$REPO/issues/$PR_NUMBER/comments" \
--jq '.[] | select(.user.login == "github-actions[bot]" and (.body | startswith("<!-- bcd-reviewdog -->"))) | .id' | head -1)

if [ -n "$COMMENT_ID" ]; then
gh api -X PATCH "repos/$REPO/issues/comments/$COMMENT_ID" -f body="$BODY"
else
gh api "repos/$REPO/issues/$PR_NUMBER/comments" -f body="$BODY"
fi

- name: Mark status as success
if: success()
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh api "$STATUS_PATH" \
-f state=success \
-f context="$STATUS_CONTEXT" \
-f description='Successful' \
-f target_url="$STATUS_TARGET"

- name: Mark status as failure
if: failure()
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh api "$STATUS_PATH" \
-f state=failure \
-f context="$STATUS_CONTEXT" \
-f description='Failing' \
-f target_url="$STATUS_TARGET"
26 changes: 25 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,31 @@ jobs:

- run: npm ci

- run: npm run lint -- --fail-on-warnings
- name: Lint
id: lint
run: npm run lint -- --fail-on-warnings

- name: Fix lint issues
id: lint-fix
if: failure() && steps.lint.conclusion == 'failure'
run: |
npm run lint:fix
if [[ -n $(git diff) ]]; then
echo "files_modified=true" >> "$GITHUB_OUTPUT"
fi

- name: Collect artifact files
if: failure() && steps.lint-fix.outputs.files_modified == 'true'
run: |
mkdir -p lint-results
git diff > lint-results/lint.diff

- name: Upload artifact
if: failure() && steps.lint-fix.outputs.files_modified == 'true'
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: lint-results
path: lint-results
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ought we specify the files to be uploaded? Seems potentially risky to upload a whole directory, where someone could upload… whatever they want? (I know it's not terribly likely, but then again my intuitions about what's dangerous about GA is… untrustworthy.)

Copy link
Copy Markdown
Contributor Author

@caugner caugner May 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test workflow runs on: pull_request, so it runs the version of the PR, and is under the control of the PR author.

We ignore all other files in the artifact, as we only use the lint.diff file here:

env -u GITHUB_ACTIONS reviewdog \
-guess \
-name="bcd-linter" \
-f=diff \
-f.diff.strip=1 \
-filter-mode=diff_context \
-reporter=github-pr-review < ../lint-results/lint.diff

The only risk is if reviewdog was vulnerable to some input, then an attacker might be able to exfiltrate the GITHUB_TOKEN, with the following permissions:

permissions:
# Download artifact from lint workflow.
actions: read
# Post inline review comments via reviewdog.
pull-requests: write
# Report commit status.
statuses: write

The advantage of having a folder artifact is that it retains the file name of the included file.


test:
runs-on: ubuntu-latest
Expand Down
6 changes: 6 additions & 0 deletions docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ For more information on the schema for feature data, see [`compat-data-schema.md

For more information on the schema for browser data, see [`browsers-schema.md`](../schemas/browsers-schema.md) and [`browsers.schema.json`](../schemas/browsers.schema.json).

## Automated lint suggestions on pull requests

When the lint check fails on a pull request, CI runs `npm run lint:fix` and posts the auto-fixable changes as inline review suggestions, attributed to `bcd-linter`. You can accept individual suggestions through GitHub's review UI, or apply all of them at once by running `npm run lint:fix` locally and committing the result.

If a suggestion looks wrong (for example, the linter is reporting a false positive), reply on the suggestion explaining why — a maintainer can help.

## Generate statistics

To see how changes will affect the statistics of exact (formerly "real") and ranged values, you can run `npm run stats [folder]`. This generates a Markdown-formatted table of the percentages of exact and ranged values for the eight primary browsers that browser-compat-data is focusing on. The script also takes an optional argument regarding a specific folder (such as `api` or `javascript`), which will print statistics result for only that folder. Additionally, you can run the script with `--all` to get statistics for all browsers tracked in BCD, not just the primary eight.
Expand Down
Loading