Skip to content

Commit 2175c9f

Browse files
Crash0v3rrid3claude
andcommitted
feat(DEVA11Y-463): composite GitHub Action (v1 scan+report)
Scaffold the public browserstack-accessibility-devtools-action repo per ADR-0035 Appendix A (GitHub App + Service Account model). - action.yml: composite action. Inputs username/access-key (required, secret), comment, check-gate, fail-on-severity, inline-suggestions, sarif, comment-mode, remediation, ai-agent (validated ^[A-Za-z0-9-]+(\[bot\])?$). Outputs result, error-count, warning-count, findings-count, comment-url, sarif-file. Hardcoded: @AccessibilityDevTools trigger, diff scope, deva11y.config.* inference, commenter write/maintain/admin permission gate. Steps implement fork-PR "diff, don't build" safety (no install/postinstall of head code; skip when OIDC/secrets restricted), pinned npx CLI (placeholder 1.3.0), Actions OIDC mint (aud placeholder browserstack-accessibility-devtools), and POST to ci-review-service /v1/findings with {platform,repo,prNumber,headSha,findings,scanProof,oidcToken} + X-CI-Platform header. The Action holds no GitHub write credential; the App posts server-side. - examples/browserstack-a11y.yml: reference consumer workflow (issue_comment, read-only token + id-token: write, concurrency, if-guard). - .github/workflows/release.yml: release stub (esbuild dist build guard, action.yml validation, manual org-admin Marketplace publish note). - README.md: public customer README (v1 = scan + report; agent hand-off preview off by default; homepage browserstack.com). - LICENSE (MIT), .gitignore. Placeholders pending provisioning: CLI version 1.3.0, OIDC audience and service URL (https://devtools-a11y-linter.browserstack.com), GitHub App. Draft PR. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 1e67b1f commit 2175c9f

6 files changed

Lines changed: 771 additions & 2 deletions

File tree

.github/workflows/release.yml

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Release pipeline for the BrowserStack Accessibility DevTools Action.
2+
#
3+
# v1 is a composite action (action.yml) that shells out to bash + the published
4+
# CLI, so there is nothing to bundle yet. This pipeline is a STUB that:
5+
# - builds any bundled `dist/` if/when the action gains JS entrypoints (esbuild),
6+
# - verifies action.yml is valid,
7+
# - documents the (manual, admin-only) Marketplace publish step.
8+
#
9+
# Marketplace publishing needs a GitHub ORG ADMIN and is intentionally NOT
10+
# automated here (see the "publish" job note below).
11+
name: Release
12+
13+
on:
14+
push:
15+
tags:
16+
- 'v*'
17+
workflow_dispatch:
18+
inputs:
19+
version:
20+
description: 'Release version tag (e.g. v1.0.0)'
21+
required: true
22+
23+
permissions:
24+
contents: read
25+
26+
jobs:
27+
build:
28+
runs-on: ubuntu-latest
29+
steps:
30+
- uses: actions/checkout@v4
31+
32+
- uses: actions/setup-node@v4
33+
with:
34+
node-version: '20'
35+
36+
# Bundle a dist/ IF the action grows JS entrypoints (esbuild pattern).
37+
# Guarded so the stub is a no-op for the current pure-composite action.
38+
- name: Build bundled dist (esbuild)
39+
shell: bash
40+
run: |
41+
set -euo pipefail
42+
if [ -f package.json ] && jq -e '.scripts.build' package.json >/dev/null 2>&1; then
43+
npm ci
44+
# Example bundling target for a future JS action entrypoint:
45+
# npx esbuild src/index.ts --bundle --platform=node \
46+
# --target=node20 --outfile=dist/index.js
47+
npm run build
48+
echo "Bundled dist/ produced."
49+
else
50+
echo "No package.json build script — pure composite action, nothing to bundle."
51+
fi
52+
53+
- name: Validate action.yml
54+
shell: bash
55+
run: |
56+
set -euo pipefail
57+
test -f action.yml || { echo "action.yml missing"; exit 1; }
58+
# Minimal structural sanity: required top-level keys present.
59+
for key in name description runs inputs outputs; do
60+
grep -qE "^${key}:" action.yml || { echo "action.yml missing '${key}:'"; exit 1; }
61+
done
62+
echo "action.yml looks structurally valid."
63+
64+
publish:
65+
needs: build
66+
runs-on: ubuntu-latest
67+
steps:
68+
- name: Marketplace publish (manual, org-admin only)
69+
shell: bash
70+
run: |
71+
echo "GitHub Marketplace publishing is a MANUAL step and requires a GitHub org admin."
72+
echo "Steps (performed in the GitHub UI by an admin):"
73+
echo " 1. Draft a release for the pushed tag."
74+
echo " 2. Tick 'Publish this Action to the GitHub Marketplace'."
75+
echo " 3. Select category (Code quality / Utilities), accept the agreement, publish."
76+
echo "This job is a placeholder and does not publish automatically."

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
node_modules/
2+
*.log
3+
.DS_Store
4+
.env
5+
# Bundled output is produced by the release pipeline when/if JS entrypoints land.
6+
dist/

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 BrowserStack
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 166 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,166 @@
1-
# browserstack-accessibility-devtools-action
2-
A package to support Accessibility Devtools Action
1+
# BrowserStack Accessibility for GitHub
2+
3+
Catch accessibility issues on your pull requests. Comment
4+
**`@AccessibilityDevTools`** on a PR and BrowserStack scans the changed code,
5+
then posts the findings right back on the PR — as a summary comment, inline
6+
suggestions, and an optional merge check.
7+
8+
- 🔎 **Scans only what changed** in the PR (fast, even on large repos).
9+
- 💬 **Results on the PR** — a summary comment and inline, one‑click suggestions.
10+
-**Optional merge gate** — fail the check when accessibility errors are found.
11+
- 🤖 **Optional agent hand‑off (preview)** — the findings comment can `@mention` a PR
12+
agent you already use, which then acts under _your_ credentials. BrowserStack runs no
13+
AI model and never handles your AI keys. **v1 is scan + report**; whether the agent
14+
reviews or fixes is up to that agent (see "Optional: agent hand-off" below for what
15+
works today).
16+
17+
Learn more: <https://browserstack.com>
18+
19+
---
20+
21+
## Prerequisites
22+
23+
1. **Install the BrowserStack Accessibility GitHub App** on your organization or
24+
repository (a one‑time step, done by a repo/org admin).
25+
2. **Add a BrowserStack Service Account key** as repository or organization
26+
**Actions secrets**:
27+
- `BROWSERSTACK_USERNAME`
28+
- `BROWSERSTACK_ACCESS_KEY`
29+
30+
A Service Account is a non‑personal, admin‑managed key built for CI — it uses
31+
no user license and can be rotated or revoked independently. (Enterprise plan.)
32+
33+
## Quick start
34+
35+
Add `.github/workflows/browserstack-a11y.yml`:
36+
37+
```yaml
38+
name: BrowserStack Accessibility
39+
on:
40+
issue_comment:
41+
types: [created]
42+
43+
permissions:
44+
contents: read # read the PR's changed files
45+
pull-requests: read # resolve the PR
46+
id-token: write # prove the run came from your CI (required)
47+
48+
concurrency:
49+
group: a11y-${{ github.event.issue.number }}
50+
cancel-in-progress: true
51+
52+
jobs:
53+
a11y:
54+
# Fires on a PR comment mentioning @AccessibilityDevTools. The action then verifies
55+
# the commenter has write/maintain/admin permission before scanning.
56+
if: >
57+
github.event.issue.pull_request &&
58+
contains(github.event.comment.body, '@AccessibilityDevTools')
59+
runs-on: ubuntu-latest
60+
timeout-minutes: 15
61+
steps:
62+
# Pin the action to a commit SHA (shown as @v1 for brevity).
63+
- uses: browserstack/browserstack-accessibility-devtools-action@<commit-sha>
64+
with:
65+
username: ${{ secrets.BROWSERSTACK_USERNAME }}
66+
access-key: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}
67+
comment: true
68+
check-gate: true
69+
fail-on-severity: error
70+
```
71+
72+
A ready-to-copy version lives at [`examples/browserstack-a11y.yml`](examples/browserstack-a11y.yml).
73+
74+
Then, on any pull request, comment:
75+
76+
```
77+
@AccessibilityDevTools
78+
```
79+
80+
The scan runs and results appear on the PR within a couple of minutes.
81+
82+
## How it works
83+
84+
1. You comment **`@AccessibilityDevTools`** on the PR.
85+
2. The workflow runs BrowserStack's accessibility CLI against the PR's changed
86+
files, authenticated by your Service Account key.
87+
3. Results are posted back to the PR **by the BrowserStack Accessibility App**
88+
(a branded bot), so your workflow's default token never needs write access.
89+
90+
> **Why `id-token: write`?** GitHub mints a short‑lived, repo‑scoped OpenID
91+
> Connect token that proves the request genuinely came from this repository's CI
92+
> run. BrowserStack verifies it before posting. It carries **no** personal
93+
> identity and grants no standing access.
94+
95+
## Inputs
96+
97+
| Input | Default | Description |
98+
| -------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
99+
| `username` | — | **Required.** Service Account username (store as a secret). |
100+
| `access-key` | — | **Required.** Service Account access key (store as a secret). |
101+
| `comment` | `true` | Post a summary findings comment on the PR. |
102+
| `check-gate` | `true` | Publish a Check Run / commit status for the PR. |
103+
| `fail-on-severity` | `error` | Fail the check at this severity and above: `error`, `warning`, or `none`. |
104+
| `inline-suggestions` | `false` | Add one‑click suggestion blocks on offending lines. |
105+
| `sarif` | `false` | Publish results to GitHub code scanning (Security tab). Uploaded by the App server-side — your workflow token needs no extra permission. |
106+
| `comment-mode` | `update` | `update` a single sticky comment across runs, or post a `new` one each time. |
107+
| `remediation` | `false` | Enable the optional agent hand‑off (preview; see below). |
108+
| `ai-agent` | — | Required when `remediation: true`. The agent's name (e.g. `coderabbitai`, `claude`); we @‑mention it. |
109+
110+
## Outputs
111+
112+
| Output | Description |
113+
| ---------------- | ------------------------------------ |
114+
| `result` | `pass` or `fail`. |
115+
| `error-count` | Number of error‑severity findings. |
116+
| `warning-count` | Number of warning‑severity findings. |
117+
| `findings-count` | Total findings. |
118+
| `comment-url` | Link to the posted PR comment. |
119+
| `sarif-file` | Path to the SARIF file (if enabled). |
120+
121+
## Optional: agent hand‑off (preview)
122+
123+
Set `remediation: true` and name your agent with `ai-agent`. When findings are
124+
posted, the App `@mentions` that agent on the PR (for example, `@coderabbitai`), and
125+
**your** agent acts under **your** credentials and billing.
126+
127+
```yaml
128+
with:
129+
username: ${{ secrets.BROWSERSTACK_USERNAME }}
130+
access-key: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}
131+
remediation: true
132+
ai-agent: coderabbitai # BrowserStack posts "@coderabbitai"
133+
```
134+
135+
What works today (honest status — this is a preview, off by default):
136+
137+
- BrowserStack **runs no AI model and never stores your AI credentials.**
138+
- The agent must accept a trigger from a **bot/App** comment.
139+
- **CodeRabbit** — responds to the mention, but produces a **review**, not a fix PR.
140+
- **Claude** (`claude-code-action` / Anthropic Claude GitHub App) — a genuine fix
141+
path, but **not yet reliable** for bot-authored triggers (upstream issues; being
142+
validated). Treat as experimental.
143+
- **GitHub Copilot's coding agent** — **not supported** (it only acts on comments from
144+
a human with write access).
145+
- If your agent ignores bot authors by default, allow‑list the **BrowserStack
146+
Accessibility** bot in its config.
147+
148+
> **v1 is scan + report.** A guaranteed AI-_fix_ path is not part of v1; the hand-off
149+
> above is a preview and is a **silent no-op** if the agent isn't configured to accept it.
150+
151+
## Privacy & security
152+
153+
- Uses a **Service Account** key (not a personal login); store it as an encrypted
154+
Actions secret and rotate via your admin.
155+
- The App's credentials stay on BrowserStack's side — they are **never** placed on
156+
your runner.
157+
- The scan reads only the pull request's changed files.
158+
159+
## Support
160+
161+
- Product & docs: <https://browserstack.com>
162+
- Accessibility API reference (including rate limits): <https://www.browserstack.com/docs/accessibility/api>
163+
164+
## License
165+
166+
[MIT](LICENSE)

0 commit comments

Comments
 (0)