Skip to main content

CI integration

Last updated

Performance budgets in CI are how regressions get caught before users see them.

The shortest version is one line of the CLI: npx rankvitals test "$DEPLOY_URL" --budget-check "performance>=90" exits non-zero when the budget is missed. The workflow further down this page does the same thing with plain curl, for pipelines that would rather not add a dependency.

The RankVitals Action

Not published yet. A first-party GitHub Action is built and waiting on publication, so there is no uses: reference to copy today — until there is, the two recipes on this page are the supported way to gate a build, and both keep working afterwards. This section will carry the workflow snippet the day the action ships.

What it will do, so you can judge whether to wait for it: one step that starts the test, waits for it, enforces your budgets, and fails the job on a breach — no GitHub App to install and no checks: write permission, because a red check is just a non-zero exit code. Budgets are written the same way the CLI writes them (performance>=90,lcp<2500), a metric the run never measured counts as a breach rather than a silent pass, and the scores, Core Web Vitals, per-budget verdicts and a report link are written to the job summary. The commit SHA is the default idempotency key, so re-running a workflow replays the stored result instead of spending a credit.

Nothing is lost by not waiting: the CLI already has the same budget syntax and the same exit codes — 0 for a pass, 3 for a breached budget — so a workflow written around npx rankvitals today only needs the step swapped out later. The changelog is where the action will be announced.

Workflow with curl

Drop this into .github/workflows/perf.yml — it tests your deploy, waits for the result, and fails the job if the performance score drops below your budget. The commit SHA doubles as an idempotency key, so re-runs don't spend extra credits.

name: Performance budget
on:
  deployment_status:
jobs:
  lighthouse:
    if: github.event.deployment_status.state == 'success'
    runs-on: ubuntu-latest
    env:
      RANKVITALS_KEY: ${{ secrets.RANKVITALS_API_KEY }}
      TARGET: ${{ github.event.deployment_status.target_url }}
      BUDGET: 80   # minimum performance score
    steps:
      - name: Run test
        run: |
          TEST_ID=$(curl -s -X POST https://rankvitals.io/api/v1/tests \
            -H "Authorization: Bearer $RANKVITALS_KEY" \
            -H "Idempotency-Key: ${{ github.sha }}" \
            -H "Content-Type: application/json" \
            -d "{\"url\": \"$TARGET\", \"device\": \"mobile\"}" | jq -r .id)
          echo "TEST_ID=$TEST_ID" >> $GITHUB_ENV
      - name: Wait for result
        run: |
          for i in $(seq 1 40); do
            BODY=$(curl -s https://rankvitals.io/api/v1/tests/$TEST_ID -H "Authorization: Bearer $RANKVITALS_KEY")
            STATUS=$(echo "$BODY" | jq -r .status)
            [ "$STATUS" = "completed" ] && { echo "$BODY" > result.json; exit 0; }
            [ "$STATUS" = "failed" ] && { echo "Test failed"; exit 1; }
            sleep 15
          done
          echo "Timed out"; exit 1
      - name: Enforce budget
        run: |
          SCORE=$(jq -r .performance_score result.json)
          GRADE=$(jq -r .grade result.json)
          REPORT=$(jq -r .report_url result.json)
          echo "Performance: $SCORE (grade $GRADE) — $REPORT"
          echo "### ⚡ RankVitals: $SCORE/100 (grade $GRADE) — [full report]($REPORT)" >> $GITHUB_STEP_SUMMARY
          [ "$SCORE" -ge "$BUDGET" ] || { echo "::error::Score $SCORE below budget $BUDGET"; exit 1; }

Choosing a budget

Set the budget a few points below your current score rather than at your target — a gate that fails on normal run-to-run variance gets disabled within a week. Lighthouse scores move by a handful of points between identical runs, so leave headroom and tighten it as the number stabilises.

On a busy repository, prefer waiting on a callback to the polling loop above; see webhooks.