GitHub Actions
Every option of the QualityGate action, with complete workflows for keyless and API key setups.
The QualityGate action is qualitygate/scan-action. It runs on GitHub-hosted
and self-hosted Linux runners and does not need a checkout of your repository.
Keyless setup with the GitHub App (recommended)
Connect the repository in the app under Projects → Connect GitHub. The workflow then needs no QualityGate secret at all:
name: QualityGate
on:
pull_request:
permissions:
contents: read
id-token: write
jobs:
quality:
runs-on: ubuntu-latest
steps:
- name: Install the scanning browser
run: npx -y playwright@1.61.1 install --with-deps chromium
- uses: qualitygate/scan-action@v1
with:
url: https://preview-${{ github.event.pull_request.number }}.example.com
id-token: write lets the runner request a token that GitHub signs, naming the
repository, branch, and workflow. QualityGate matches it to your project by
GitHub’s numeric repository id, which survives renames and is never reused.
Each run also asks GitHub whether the App is still installed, so uninstalling
the App stops keyless runs immediately. The App posts the pull request comment,
so the workflow needs neither pull-requests: write nor a GITHUB_TOKEN.
Setup with an API key
Use an API key when you cannot install the GitHub App, or for GitLab and
Bitbucket. Create the project in the app, create a key on the project card, and
store it as the repository secret QUALITYGATE_API_KEY
(Settings → Secrets and variables → Actions).
name: QualityGate
on:
pull_request:
permissions:
contents: read
pull-requests: write
id-token: write
jobs:
quality:
runs-on: ubuntu-latest
steps:
- name: Install the scanning browser
run: npx -y playwright@1.61.1 install --with-deps chromium
- uses: qualitygate/scan-action@v1
env:
GITHUB_TOKEN: ${{ github.token }}
with:
api_key: ${{ secrets.QUALITYGATE_API_KEY }}
url: ${{ vars.QUALITYGATE_PREVIEW_URL }}
bundle: a11y
fail_on: critical=0,serious=0
Without the GitHub App, the action posts the comment itself as
github-actions[bot], which is why this version grants pull-requests: write
and passes GITHUB_TOKEN. id-token: write is optional here but recommended:
it sends proof of the repository along with the key. See
Repository identity.
Inputs
| Input | Default | Description |
|---|---|---|
url | required | HTTP or HTTPS address to scan. Its host must be one of the project’s allowed domains. |
api_key | none | Project API key. Required unless the repository is connected through the GitHub App and id-token: write is granted. |
bundle | a11y | Which checks to run. See Bundles and checks. |
fail_on | critical=0,serious=0 | Limits for the gate, or regression. See Gates and regression mode. |
wait_for_url | true | Poll the URL until it returns a 2xx status before scanning. |
wait_timeout | 120 | Seconds to wait for the URL. A positive whole number. |
comment | true | Create or update the QualityGate comment on the pull request. |
Pin @v1 to receive compatible fixes automatically, or pin an exact release
such as @v1.0.0 if your organization requires immutable references.
Permissions
| Permission | Needed for |
|---|---|
contents: read | The default for most workflows. QualityGate does not read your code. |
id-token: write | Keyless runs, and proof of repository identity for key-based runs. |
pull-requests: write | Only when the action posts its own comment, that is, without the GitHub App. |
Supported triggers
| Event | What the run does |
|---|---|
pull_request | Scans the preview, comments on the pull request, and gates the check. |
push | On the project’s default branch, records the baseline that regression mode compares against. No comment. |
schedule | A scheduled scan with its own monthly allowance. No comment, never a baseline. |
Other events, including workflow_dispatch and deployment_status, are
rejected. pull_request_target is deliberately unsupported: it runs with
write access and secrets in the context of untrusted fork code.
Using a preview URL from another job
If a job in the same workflow deploys the preview, expose its URL as an output and make the scan depend on it:
jobs:
deploy:
runs-on: ubuntu-latest
outputs:
url: ${{ steps.deploy.outputs.url }}
steps:
- uses: actions/checkout@v5
- id: deploy
run: echo "url=$(./scripts/deploy-preview.sh)" >> "$GITHUB_OUTPUT"
quality:
needs: deploy
runs-on: ubuntu-latest
steps:
- run: npx -y playwright@1.61.1 install --with-deps chromium
- uses: qualitygate/scan-action@v1
with:
url: ${{ needs.deploy.outputs.url }}
Ready-made versions for Vercel, Netlify, Render, and Cloudflare Pages are in Preview deployments.
The pull request comment
QualityGate keeps exactly one comment per pull request and updates it on every
run, so a busy pull request does not fill up with reports. With the GitHub App
installed, the comment comes from the QualityGate App with its own avatar.
Without it, the action falls back to github-actions[bot] using the
workflow’s token.
A comment that cannot be posted is logged as a warning and never changes the
result. The full report is always written to the job summary as well. Set
comment: 'false' to skip the comment and keep only the job summary.
Advisory mode that never blocks
To try QualityGate without blocking anyone, let the step fail without failing the job:
- run: npx -y playwright@1.61.1 install --with-deps chromium
- uses: qualitygate/scan-action@v1
continue-on-error: true
with:
url: ${{ vars.QUALITYGATE_PREVIEW_URL }}
The comment and job summary still show the real verdict; GitHub simply shows
the job as passed. Remove continue-on-error when you are ready to enforce.
Fork pull requests
GitHub gives workflows triggered by forks no secrets, a read-only token, and no
OIDC token. A scan from a fork therefore cannot authorize and fails closed.
Run QualityGate on branches in your own repository, or re-run the change from a
branch once it has been reviewed. Do not switch to pull_request_target to
work around this.
Repository identity
An API key alone says which project is asking, but not from which repository.
Granting id-token: write adds a GitHub-signed statement of the repository,
which QualityGate checks against the project. It costs nothing: the token is
scoped to QualityGate, expires in minutes, and grants no access to your
repository.
Once every workflow for a project sends it, contact us to switch the project to require it. From then on, a copied key used from any other repository is refused.
Self-hosted runners
Self-hosted runners work if they run Linux on a runner version that supports
node24 actions, can install Chromium with Playwright, and can reach both your preview
and https://api.qualitygate.dev. Drop --with-deps from the install step if
the runner cannot use sudo apt-get, and install Chromium’s system libraries
in the runner image instead.