CI

GitHub Action

Re-check your app every time a migration lands on your default branch. The action starts a PatchLabs check, waits for the lab result, prints it in the job summary, and fails the build if strangers can read personal data.

Set up in three steps

  1. Add and verify the app

    Sign in, add the repo as an app and commit its verification file. See monitoring. Copy the app’s ID from its page.

  2. Create an API key

    In Settings → API keys, create a key named, say, “GitHub Actions”. It’s shown once. In your repo, open Settings → Secrets and variables → Actions and add it as the secret PATCHLABS_API_KEY. Add the app ID as the variable PATCHLABS_APP_ID.

  3. Add the workflow

    Save this as .github/workflows/patchlabs.yml:

    .github/workflows/patchlabs.yml
    name: PatchLabs
    on:
      push:
        branches: [main]
        paths: ['supabase/migrations/**']
      workflow_dispatch:
    
    concurrency: patchlabs
    
    jobs:
      leak-check:
        runs-on: ubuntu-latest
        steps:
          - uses: manra399/patchlabs/action@main
            with:
              api-key: ${{ secrets.PATCHLABS_API_KEY }}
              app-id: ${{ vars.PATCHLABS_APP_ID }}
              wait: true
              fail-on-leak: true

No checkout step is needed: PatchLabs reads the repo from GitHub. concurrency stops two runs from overlapping, since an app can only have one check open at a time.

What you see

leak-check · Run manra399/patchlabs/action@main
PatchLabs check started: 7c1e2f0a-…
Status: queued
Status: running
 
PatchLabs · you/shop-app
  result         Leaks found; a lab-proven
                  fix is ready
  tables leaking  profiles
  lab proof       passed
  report          https://patchlabs.dev/check?id=…
 
Error: Leaks found … Apply the fix, then re-run.
step failedExample output with fail-on-leak: true

The same result goes into the job summary as a small table with a link to the report. After you apply the fix, re-run the job: it passes once the check reads no_leaks.

Inputs

InputDefaultWhat it does
api-keyrequiredWorkspace API key (pl_live_…). Pass it from a secret.
app-idrequiredThe monitored app’s ID.
waittrueWait for the result. false starts the check and ends the step straight away.
fail-on-leaktrueFail the step when the finished check (done or needs_review) has leaking tables your team hasn’t marked as intended.
timeout-minutes30How long to wait for a result.
fail-on-timeoutfalseFail instead of warn when no result arrives in time.
poll-seconds15Seconds between status requests (minimum 5).
api-urlhttps://api.patchlabs.devAPI base URL.

Outputs

OutputValue
check-idThe check this run started
statusdone, no_leaks, needs_review or failed; queued/running if not waited for or timed out; busy or limit if no check could start
urlLink to the report
leaks-foundtrue or false
tables-leakingComma-separated table names
suppressed-tablesLeaking tables your team marked as intended (not counted by fail-on-leak)
lab-proof-passedtrue when the fix passed the lab proof

Use them in later steps, for example to post the report link somewhere:

workflow steps
      - id: patchlabs
        uses: manra399/patchlabs/action@main
        with:
          api-key: ${{ secrets.PATCHLABS_API_KEY }}
          app-id: ${{ vars.PATCHLABS_APP_ID }}
          fail-on-leak: false
      - if: steps.patchlabs.outputs.leaks-found == 'true'
        run: echo "Leaking tables: ${{ steps.patchlabs.outputs.tables-leaking }} → ${{ steps.patchlabs.outputs.url }}"

How each result is handled

SituationStep result
no_leakspasses
done or needs_reviewfails with fail-on-leak: true, otherwise warns
Only tables marked as intended are leakingPasses with a notice
failed (the check couldn’t finish)Warns. We’re alerted and follow up
A check is already open for the appWarns and passes; no new check is started
Monthly run limit reachedWarns and passes
App not verified, bad key, wrong app IDFails with the reason
No result within timeout-minutesWarns, or fails with fail-on-timeout: true

On a schedule

Monitoring already re-checks your app weekly or monthly. If you’d rather see results in GitHub, add a cron trigger:

on:
on:
  schedule:
    - cron: '0 6 * * 1'   # Mondays at 06:00 UTC
  workflow_dispatch:

How it works

The action is a small shell script that uses curl and jq (both preinstalled on GitHub-hosted Linux and macOS runners). It calls two endpoints with your key:

  1. POST /v1/apps/{app_id}/checks to start a check;
  2. GET /v1/checks/{id} every poll-seconds until finished is true.

The source is in the action/ folder of the manra399/patchlabs repo. Read it before you use it; it’s about 130 lines. For a fixed version, pin a commit SHA instead of @main.

Good to know

A check reads your default branch as it is on GitHub, not the branch of a pull request. Run the action after changes land on your default branch; on a pull_request trigger it would report on the default branch, not the PR. Each run uses one of your plan’s monthly manual runs (Free 4, Maker 30, Agency 200). Tables your team marked as intended don’t count for fail-on-leak: the step fails only while other leaking tables remain, and lists the intended ones separately.

Updated 10 Nov 2026Something unclear or wrong? Tell the founder