Skip to content

trunk-merge/pr-106380/c25d1bc2-cb62-4e5a-85ac-178e2a839002 #13196

trunk-merge/pr-106380/c25d1bc2-cb62-4e5a-85ac-178e2a839002

trunk-merge/pr-106380/c25d1bc2-cb62-4e5a-85ac-178e2a839002 #13196

name: Desktop React Doctor
# Advisory only: posts a sticky PR comment with findings for changed desktop
# files and never fails the PR. The source repo's --blocking enforcement step
# is deliberately not ported.
on:
pull_request:
types: [opened, synchronize, reopened]
# Workflow-level paths only work because react-doctor is not a required
# check. If it ever becomes required, move the skip into a gate job like
# desktop-ci.yml, or path-skipped PRs will wait on it forever.
paths:
- 'products/desktop/**/*.ts'
- 'products/desktop/**/*.tsx'
- 'products/desktop/**/*.jsx'
- '.github/workflows/desktop-react-doctor.yml'
- '.github/scripts/desktop/react-doctor-comment.mjs'
permissions:
contents: read
pull-requests: write
concurrency:
group: desktop-react-doctor-${{ github.head_ref || github.ref }}
cancel-in-progress: true
jobs:
react-doctor:
runs-on: depot-ubuntu-24.04
timeout-minutes: 15
# Skipped on trunk-merge/** heads: merge-queue PRs are ephemeral and every
# constituent PR already ran this in full.
if: ${{ !startsWith(github.head_ref, 'trunk-merge/') }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
filter: blob:none
persist-credentials: false
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 22
package-manager-cache: false
- id: scan
name: Run react-doctor on changed desktop files
shell: bash
working-directory: products/desktop
env:
NO_COLOR: '1'
REACT_DOCTOR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
# -e omitted: exit codes are captured and inspected explicitly.
set -uo pipefail
CHANGED="${RUNNER_TEMP}/react-doctor-changed-files.txt"
# --relative: react-doctor runs from products/desktop, so the changed
# file list must be relative to it, not to the repo root.
if ! git diff --name-only --relative --diff-filter=ACMR "${REACT_DOCTOR_BASE_SHA}...${HEAD_SHA}" -- . > "$CHANGED"; then
echo "Could not diff ${REACT_DOCTOR_BASE_SHA}...${HEAD_SHA}; failing rather than skipping the scan." >&2
echo "exit-code=1" >> "$GITHUB_OUTPUT"
exit 1
fi
if [ ! -s "$CHANGED" ]; then
echo "No changed files; nothing for react-doctor to scan."
echo "exit-code=0" >> "$GITHUB_OUTPUT"
exit 0
fi
REPORT="${RUNNER_TEMP}/react-doctor-report.json"
status=0
# Pinned: products/desktop/.npmrc's minimum release age makes @latest
# intermittently unresolvable in CI. Bump deliberately, not via latest.
npx --yes react-doctor@0.9.1 . --blocking error --changed-files-from "$CHANGED" --json --json-compact --no-telemetry > "$REPORT" || status=$?
echo "exit-code=$status" >> "$GITHUB_OUTPUT"
echo "report=$REPORT" >> "$GITHUB_OUTPUT"
if [ "$status" -ne 0 ]; then
echo "react-doctor exited with status ${status}; advisory only, not failing the job." >&2
cat "$REPORT" >&2 || true
fi
- name: Render report summary
id: render
if: ${{ always() && steps.scan.outputs.report != '' }}
shell: bash
env:
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_SERVER_URL: ${{ github.server_url }}
REACT_DOCTOR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
REACT_DOCTOR_PATH_PREFIX: products/desktop
REPORT: ${{ steps.scan.outputs.report }}
run: |
set -euo pipefail
BODY="${RUNNER_TEMP}/react-doctor-comment.md"
node "${GITHUB_WORKSPACE}/.github/scripts/desktop/react-doctor-comment.mjs" "$REPORT" "$BODY"
cat "$BODY" >> "$GITHUB_STEP_SUMMARY"
echo "body-path=$BODY" >> "$GITHUB_OUTPUT"
- name: Upsert sticky PR comment
# Fork PRs only get a read-only token, so the upsert would fail there;
# skip it explicitly instead of failing silently. Fork contributors
# still get the report via the job summary above.
if: ${{ always() && steps.render.outputs.body-path != '' && github.event.pull_request.head.repo.full_name == github.repository }}
continue-on-error: true
shell: bash
env:
GH_TOKEN: ${{ github.token }}
GITHUB_REPOSITORY: ${{ github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number }}
BODY: ${{ steps.render.outputs.body-path }}
run: |
set -uo pipefail
jq -Rs '{body: .}' "$BODY" > "${RUNNER_TEMP}/react-doctor-payload.json"
marker="<!-- desktop-react-doctor:summary -->"
existing=$(gh api "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" --paginate \
--jq ".[] | select(.body | startswith(\"${marker}\")) | .id" | head -n1 || true)
if [ -n "$existing" ]; then
gh api -X PATCH "repos/${GITHUB_REPOSITORY}/issues/comments/${existing}" --input "${RUNNER_TEMP}/react-doctor-payload.json" >/dev/null
else
gh api -X POST "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" --input "${RUNNER_TEMP}/react-doctor-payload.json" >/dev/null
fi