Trigger a WCAGdesk accessibility scan from your CI pipeline, wait for it to finish, and surface the findings as SARIF in GitHub's Security tab and PR Checks.
Built for European Accessibility Act (EAA) / BFSG compliance work — every scan is RFC 3161 timestamped, hash-chained, and (optionally) Polygon-anchored, so the CI run produces a court-grade record alongside the engineering signal.
name: a11y
on:
pull_request:
push:
branches: [main]
jobs:
scan:
runs-on: ubuntu-latest
permissions:
security-events: write # for upload-sarif
contents: read
steps:
- uses: sergordienko/wcagdesk-action@v1
id: wcagdesk
with:
site-id: ${{ vars.WCAGDESK_SITE_ID }}
api-key: ${{ secrets.WCAGDESK_API_KEY }}
# Optional knobs:
# wait: true # block until scan finishes (default true)
# fail-on: critical # comma-separated severities that fail the job
# timeout-seconds: 1800 # max wait time when wait=true
- uses: github/codeql-action/upload-sarif@v3
if: always() && steps.wcagdesk.outputs.sarif-path != ''
with:
sarif_file: ${{ steps.wcagdesk.outputs.sarif-path }}
category: wcagdeskThat's it. PRs that introduce a critical issue will fail the check; all
findings show up annotated on changed lines in the GitHub PR review.
- A WCAGdesk site. Add your domain in Settings → Sites and verify ownership.
- An API key. Generate one at
Settings → CI / API keys.
The raw value (
wcd_…) is shown once — copy it into your repo assecrets.WCAGDESK_API_KEYimmediately. - The site ID. Visible in the site detail page URL
(
/dashboard/#/sites/<id>). Store it asvars.WCAGDESK_SITE_ID(a regular repo variable — not a secret).
| name | required | default | description |
|---|---|---|---|
site-id |
✓ | — | WCAGdesk site UUID. |
api-key |
✓ | — | API key from Settings → CI / API keys. |
wait |
true |
Block until the scan finishes. Set to false to fire-and-forget. |
|
timeout-seconds |
1800 |
Polling timeout when wait=true. |
|
api-url |
https://wcagdesk.eu |
Override for staging / self-hosted. | |
fail-on |
critical |
Comma-separated severities that fail the job. Valid: critical,serious,moderate,minor. Use none to disable. |
|
sarif-output |
wcagdesk.sarif |
Path to write the SARIF file. |
| name | description |
|---|---|
scan-id |
Identifier of the scan that ran. |
status |
done / failed / queued / running. |
issue-count |
Total issues. |
severity-score |
Weighted severity (critical×10 + serious×5 + moderate×2 + minor×1). |
diff-new-count |
New issues vs the previous completed scan. |
diff-fixed-count |
Issues fixed vs the previous completed scan. |
sarif-path |
Path of the downloaded SARIF (empty when wait=false). |
- uses: sergordienko/wcagdesk-action@v1
id: wcagdesk
with:
site-id: ${{ vars.WCAGDESK_SITE_ID }}
api-key: ${{ secrets.WCAGDESK_API_KEY }}
fail-on: none
- name: Fail on regression vs main
if: steps.wcagdesk.outputs.diff-new-count > 0
run: |
echo "::error::WCAGdesk introduced ${{ steps.wcagdesk.outputs.diff-new-count }} new issue(s)."
exit 1- uses: sergordienko/wcagdesk-action@v1
id: wcagdesk
with:
site-id: ${{ vars.WCAGDESK_SITE_ID }}
api-key: ${{ secrets.WCAGDESK_API_KEY }}
- uses: actions/github-script@v7
if: github.event_name == 'pull_request'
with:
script: |
const body = [
'**WCAGdesk accessibility scan**',
'',
`Issues: ${{ steps.wcagdesk.outputs.issue-count }} · severity score ${{ steps.wcagdesk.outputs.severity-score }}`,
`Delta vs previous: +${{ steps.wcagdesk.outputs.diff-new-count }} new · -${{ steps.wcagdesk.outputs.diff-fixed-count }} fixed`,
'',
`Scan record: https://wcagdesk.eu/dashboard/#/scans/${{ steps.wcagdesk.outputs.scan-id }}`,
].join('\n');
await github.rest.issues.createComment({
...context.repo,
issue_number: context.payload.pull_request.number,
body,
});- uses: sergordienko/wcagdesk-action@v1
with:
site-id: ${{ vars.WCAGDESK_SITE_ID }}
api-key: ${{ secrets.WCAGDESK_API_KEY }}
wait: 'false'The action returns immediately after queueing. Useful when you want scheduled scans driven from CI cron without blocking developer flow.
- The action only depends on
curlandjq, which are pre-installed on GitHub-hosted runners. No Node, no Docker, no third-party dependencies. - The
Authorization: Bearer wcd_…value is never logged. Treat it as you would any other API secret — never commit it, rotate periodically. - WCAGdesk endpoints used:
POST /api/ci/scan,GET /api/ci/scans/:id,GET /api/ci/scans/:id.sarif,GET /api/scans/:id?impact=…. - Each scan is RFC 3161 timestamped and hash-chained. The full verify
bundle is downloadable from the dashboard or via
GET /api/scans/:id/verify-bundle.
MIT. See LICENSE.
- Documentation: https://wcagdesk.eu/methodology.html
- Crypto-evidence spec: https://wcagdesk.eu/crypto-evidence.html
- Issues: open one on this repo.
- Commercial questions:
hello@wcagdesk.eu.