name: "frontend-profiling" on: pull_request: types: [opened, synchronize, reopened] concurrency: group: "frontend-profiling-${{ github.event.pull_request.number }}" cancel-in-progress: true permissions: {} jobs: benchmark: env: UV_EXCLUDE_NEWER: 7 days PLAYWRIGHT_VERSION: 1.60.0 permissions: contents: read pull-requests: write name: "frontend-benchmark" runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: "gradio-app/gradio/.github/actions/changes@main" id: changes with: filter: "js" token: ${{ secrets.GITHUB_TOKEN }} - name: Find latest release tag if: steps.changes.outputs.should_run == 'true' id: latest_tag run: echo "tag=$(git tag --list 'gradio@*' --sort=-v:refname | head -n 1)" >> "$GITHUB_OUTPUT" - name: Setup Python if: steps.changes.outputs.should_run == 'true' uses: actions/setup-python@v5 with: python-version: "3.10" - name: Install uv if: steps.changes.outputs.should_run == 'true' run: curl -LsSf https://astral.sh/uv/0.11.3/install.sh | sh - name: Install pnpm if: steps.changes.outputs.should_run == 'true' uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # @v4 with: version: 10.17.0 - name: Setup Node.js if: steps.changes.outputs.should_run == 'true' uses: actions/setup-node@v4 with: node-version: 24 cache: pnpm cache-dependency-path: pnpm-lock.yaml # Install the benchmark "base" (latest release) straight from PyPI instead # of checking out the tag and rebuilding it — the published wheel already # ships the built frontend (gradio/templates), so this drops the ~4min base # `pnpm build` on every run. We stay on the PR checkout the whole job: the # benchmark spec + demo are the PR's versions for both runs, and the demo is # launched as `python .py` (the runner/demo dir is on sys.path, not # the cwd), so `import gradio` resolves to the installed package — the wheel # here, the editable PR build later. Base and PR use separate venvs so the # PR's editable install can't shadow the base wheel. - name: Install base gradio from the published release (base) if: steps.changes.outputs.should_run == 'true' run: | export PATH="$HOME/.cargo/bin:$PATH" uv venv --python=3.10 venv_base . venv_base/bin/activate TAG="${{ steps.latest_tag.outputs.tag }}" uv pip install "gradio==${TAG#gradio@}" - name: Install frontend test deps (base) if: steps.changes.outputs.should_run == 'true' # node_modules is only needed to run the Playwright benchmark (no build). run: | pnpm i --frozen-lockfile --ignore-scripts pnpm add -Dw --ignore-scripts playwright@$PLAYWRIGHT_VERSION @playwright/test@$PLAYWRIGHT_VERSION - name: Cache Playwright Browsers if: steps.changes.outputs.should_run == 'true' uses: actions/cache@v4 with: path: ~/.cache/ms-playwright key: playwright-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }} restore-keys: | playwright-${{ runner.os }}- - name: Install Playwright (base) if: steps.changes.outputs.should_run == 'true' timeout-minutes: 3 run: pnpm exec playwright install chromium --only-shell - name: Run benchmark (base) if: steps.changes.outputs.should_run == 'true' run: | . venv_base/bin/activate PERF_RESULTS_FILE=/tmp/bench_base.json pnpm exec playwright test \ --config .config/playwright.config.js \ js/spa/test/big_complex_demo.spec.ts - name: Install and build PR if: steps.changes.outputs.should_run == 'true' run: | export PATH="$HOME/.cargo/bin:$PATH" uv venv --allow-existing venv --python=3.10 . venv/bin/activate uv pip install -e client/python uv pip install -e ".[oauth,mcp]" pnpm install --no-frozen-lockfile pnpm css && pnpm build - name: Install Playwright (PR) if: steps.changes.outputs.should_run == 'true' timeout-minutes: 3 run: pnpm exec playwright install chromium --only-shell - name: Run benchmark (PR) if: steps.changes.outputs.should_run == 'true' run: | . venv/bin/activate PERF_RESULTS_FILE=/tmp/bench_pr.json pnpm exec playwright test \ --config .config/playwright.config.js \ js/spa/test/big_complex_demo.spec.ts - name: Compare results id: compare if: steps.changes.outputs.should_run == 'true' env: BASE_TAG: ${{ steps.latest_tag.outputs.tag }} run: node scripts/compare_frontend_benchmarks.js - name: Post or update PR comment if: steps.compare.outputs.failed == 'true' uses: actions/github-script@v7 with: script: | const fs = require('fs'); const body = fs.readFileSync('/tmp/bench_comment.md', 'utf8'); const marker = ''; const commentBody = marker + '\n' + body; const { data: comments } = await github.rest.issues.listComments({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, }); const existing = comments.find(c => c.body.includes(marker)); if (existing) { await github.rest.issues.updateComment({ owner: context.repo.owner, repo: context.repo.repo, comment_id: existing.id, body: commentBody, }); } else { await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, body: commentBody, }); }