Skip to content

Commit 84a8f6d

Browse files
new release (v3.3.0) (#243)
[passed checks](https://github.com/github/accessibility-scorecard/actions/runs/28392441171) Cuts a new minor release to ship features and fixes accumulated in `main` since v3.2.0. This is a minor bump — all changes are additive and backwards-compatible (no inputs/outputs removed or reformatted). ### What's new - **`group_by` input** — consolidate findings into issues by `finding` (default), `rule`, or `rule+url` - **`dry_run` input** — preview what would be filed/closed/reopened without mutating issues or writing the cache - **`file_best_practice_issues` / `file_experimental_issues` inputs** — toggle filing of non-WCAG findings - **Finding categorization** — Axe violations tagged `wcag` / `best-practice` / `experimental`, surfaced in issue bodies and labels - **`wontfix` label support** — closed issues labeled `wontfix` are no longer reopened on re-runs ### Fixes - Reflow-scan messaging reworded and updated to reference WCAG 2.2 - Issue acceptance criteria updated from WCAG 2.1 → 2.2 - `actions/checkout@v6 → v7` across workflows and `gh-cache/*` - Dependency bumps (`@types/node`, `eslint`, `typescript-eslint`, `vitest`, `playwright`, etc.) ### Testing - ✅ Scorecard manual test against `@main` passed — [run #83](https://github.com/github/accessibility-scorecard/actions/runs/28392441171), 3-site matrix (storybook, admin, thehub), exercising `auth_context` + auth-to-github - ✅ Automated Test workflow green on `main` - ⚠️ Sandbox-only checks (self-hosted runners, first-run/no-cache, Copilot assignment) were **not** run due to repo access limitations - ⚠️ New inputs (`dry_run`, `group_by`, `file_*`, `wontfix`) are covered by unit tests but were not exercised in a live sandbox run closes github/accessibility#10808
2 parents 1b95023 + 55f52f1 commit 84a8f6d

42 files changed

Lines changed: 1456 additions & 341 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/actions/auth/package-lock.json

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.github/actions/auth/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"playwright": "^1.60.0"
1818
},
1919
"devDependencies": {
20-
"@types/node": "^25.7.0",
20+
"@types/node": "^25.9.0",
2121
"typescript": "^6.0.3"
2222
}
2323
}

.github/actions/file/README.md

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Files GitHub issues to track potential accessibility gaps.
1111
**Required** Path to a JSON file containing the list of potential accessibility gaps. The path can be absolute or relative to the working directory (which defaults to `GITHUB_WORKSPACE`). For example: `findings.json`.
1212

1313
The file should contain a JSON array of finding objects. For example:
14+
1415
```json
1516
[]
1617
```
@@ -28,27 +29,49 @@ The file should contain a JSON array of finding objects. For example:
2829
**Optional** Path to a JSON file containing cached filings from previous runs. The path can be absolute or relative to the working directory (which defaults to `GITHUB_WORKSPACE`). Without this, duplicate issues may be filed. For example: `cached-filings.json`.
2930

3031
The file should contain a JSON array of filing objects. For example:
32+
3133
```json
3234
[
3335
{
3436
"findings": [],
35-
"issue": {"id":1,"nodeId":"SXNzdWU6MQ==","url":"https://github.com/github/docs/issues/123","title":"Accessibility issue: 1"}
37+
"issue": {
38+
"id": 1,
39+
"nodeId": "SXNzdWU6MQ==",
40+
"url": "https://github.com/github/docs/issues/123",
41+
"title": "Accessibility issue: 1"
42+
}
3643
}
3744
]
3845
```
3946

47+
#### `group_by`
48+
49+
**Optional** How to consolidate findings into issues. One of:
50+
51+
- `finding` (default): one issue per individual violation — current behavior, unchanged.
52+
- `rule`: one issue per rule (`ruleId`/`scannerType`), aggregating every occurrence across all scanned URLs.
53+
- `rule+url`: one issue per rule per scanned URL.
54+
55+
When grouping, each additional occurrence is appended to the single "umbrella" issue body as a checklist item under an **Occurrences** section rather than spawning a new issue. This is the preferred mechanism for consolidating issues over `open_grouped_issues`.
56+
4057
### Outputs
4158

4259
#### `filings_file`
4360

4461
Absolute path to a JSON file containing the list of issues filed (and their associated finding(s)). The action writes this file to a temporary directory and returns the absolute path. For example: `$RUNNER_TEMP/filings-<uuid>.json`.
4562

4663
The file will contain a JSON array of filing objects. For example:
64+
4765
```json
4866
[
4967
{
5068
"findings": [],
51-
"issue": {"id":1,"nodeId":"SXNzdWU6MQ==","url":"https://github.com/github/docs/issues/123","title":"Accessibility issue: 1"}
69+
"issue": {
70+
"id": 1,
71+
"nodeId": "SXNzdWU6MQ==",
72+
"url": "https://github.com/github/docs/issues/123",
73+
"title": "Accessibility issue: 1"
74+
}
5275
}
5376
]
5477
```

.github/actions/file/action.yml

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,54 @@
1-
name: "File"
2-
description: "Files GitHub issues to track potential accessibility gaps."
1+
name: 'File'
2+
description: 'Files GitHub issues to track potential accessibility gaps.'
33

44
inputs:
55
findings_file:
6-
description: "Path to a JSON file containing the list of potential accessibility gaps"
6+
description: 'Path to a JSON file containing the list of potential accessibility gaps'
77
required: true
88
repository:
9-
description: "Repository (with owner) to file issues in"
9+
description: 'Repository (with owner) to file issues in'
1010
required: true
1111
token:
1212
description: "Token with fine-grained permission 'issues: write'"
1313
required: true
1414
base_url:
15-
description: "Optional base URL to pass into Octokit for the GitHub API (for example, `https://YOUR_HOSTNAME/api/v3` for GitHub Enterprise Server)"
15+
description: 'Optional base URL to pass into Octokit for the GitHub API (for example, `https://YOUR_HOSTNAME/api/v3` for GitHub Enterprise Server)'
1616
required: false
1717
cached_filings_file:
18-
description: "Path to a JSON file containing cached filings from previous runs. Without this, duplicate issues may be filed."
18+
description: 'Path to a JSON file containing cached filings from previous runs. Without this, duplicate issues may be filed.'
1919
required: false
2020
screenshot_repository:
2121
description: "Repository (with owner) where screenshots are stored on the gh-cache branch. Defaults to the 'repository' input if not set. Required if issues are open in a different repo to construct proper screenshot URLs."
2222
required: false
2323
open_grouped_issues:
2424
description: "In the 'file' step, also open grouped issues which link to all issues with the same root cause"
2525
required: false
26-
default: "false"
26+
default: 'false'
27+
group_by:
28+
description: "How to group findings into issues: 'finding' (one issue per violation, default), 'rule' (one issue per rule), or 'rule+url' (one issue per rule per scanned URL)."
29+
required: false
30+
default: 'finding'
31+
dry_run:
32+
description: 'When true, log the issues that would be filed without opening, closing, or reopening any issues.'
33+
required: false
34+
default: 'false'
35+
file_best_practice_issues:
36+
description: 'File issues for best-practice findings (accessibility recommendations that are not hard WCAG failures). Disabling only suppresses new issues; existing ones are left untouched.'
37+
required: false
38+
default: 'true'
39+
file_experimental_issues:
40+
description: 'File issues for experimental findings (checks that are not yet stable). Disabling only suppresses new issues; existing ones are left untouched.'
41+
required: false
42+
default: 'true'
2743

2844
outputs:
2945
filings_file:
30-
description: "Path to a JSON file containing the list of issues filed (and their associated finding(s))"
46+
description: 'Path to a JSON file containing the list of issues filed (and their associated finding(s))'
3147

3248
runs:
33-
using: "node24"
34-
main: "bootstrap.js"
49+
using: 'node24'
50+
main: 'bootstrap.js'
3551

3652
branding:
37-
icon: "compass"
38-
color: "blue"
53+
icon: 'compass'
54+
color: 'blue'

.github/actions/file/package-lock.json

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.github/actions/file/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"@octokit/plugin-throttling": "^11.0.3"
1919
},
2020
"devDependencies": {
21-
"@types/node": "^25.7.0",
21+
"@types/node": "^25.9.0",
2222
"typescript": "^6.0.3"
2323
}
2424
}

.github/actions/file/src/generateIssueBody.ts

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import type {Finding} from './types.d.js'
22

3-
export function generateIssueBody(finding: Finding, screenshotRepo: string): string {
3+
export function generateIssueBody(occurrences: Finding | Finding[], screenshotRepo: string): string {
4+
const findings = Array.isArray(occurrences) ? occurrences : [occurrences]
5+
const finding = findings[0]
6+
47
const solutionLong = finding.solutionLong
58
?.split('\n')
69
.map((line: string) =>
@@ -18,21 +21,64 @@ export function generateIssueBody(finding: Finding, screenshotRepo: string): str
1821
`
1922
}
2023

24+
let occurrencesSection = ''
25+
if (findings.length > 1) {
26+
const items = findings.map(f => `- [ ] ${f.html ? `\`${f.html}\` on ${f.url}` : f.url}`).join('\n')
27+
occurrencesSection = `
28+
## ${findings.length} Other Occurrences:
29+
30+
${items}
31+
`
32+
}
33+
34+
const categoryNotice =
35+
finding.category && finding.category !== 'wcag'
36+
? `> [!NOTE]\n> This is ${
37+
finding.category === 'experimental' ? 'an experimental check' : 'a best-practice recommendation'
38+
}, not a definite WCAG failure.\n\n`
39+
: ''
40+
41+
const standardsLine =
42+
finding.category && finding.category !== 'wcag'
43+
? '- [ ] The fix MUST meet the accessibility standards specified by the repository or organization (WCAG 2.2 if applicable).'
44+
: '- [ ] The fix MUST meet WCAG 2.2 guidelines OR the accessibility standards specified by the repository or organization.'
45+
2146
const acceptanceCriteria = `## Acceptance Criteria
2247
- [ ] The specific violation reported in this issue is no longer reproducible.
23-
- [ ] The fix MUST meet WCAG 2.1 guidelines OR the accessibility standards specified by the repository or organization.
48+
${standardsLine}
2449
- [ ] A test SHOULD be added to ensure this specific violation does not regress.
2550
- [ ] This PR MUST NOT introduce any new accessibility issues or regressions.`
2651

27-
const body = `## What
28-
An accessibility scan ${finding.html ? `flagged the element \`${finding.html}\`` : `found an issue on ${finding.url}`} because ${finding.problemShort}. Learn more about why this was flagged by visiting ${finding.problemUrl}.
52+
const body = `${categoryNotice}## What
53+
${describeFinding(finding)}
2954
3055
${screenshotSection ?? ''}
3156
To fix this, ${finding.solutionShort}.
3257
${solutionLong ? `\nSpecifically:\n\n${solutionLong}` : ''}
33-
58+
${occurrencesSection}
3459
${acceptanceCriteria}
3560
`
3661

3762
return body
3863
}
64+
65+
function describeFinding(finding: Finding): string {
66+
const reason = `because ${finding.problemShort}. Learn more about why this was flagged by visiting ${finding.problemUrl}.`
67+
68+
// Axe carries every failing element; list them all, not just the first.
69+
if (finding.nodes && finding.nodes.length > 0) {
70+
const count = finding.nodes.length
71+
const subject = count === 1 ? 'an element' : `${count} elements`
72+
const elementList = finding.nodes
73+
.map(node => `- \`${node.html}\`${node.target ? ` (selector: \`${node.target}\`)` : ''}`)
74+
.join('\n')
75+
const heading = count === 1 ? 'The following element needs' : 'The following elements need'
76+
return `An accessibility scan flagged ${subject} on ${finding.url} ${reason}\n\n${heading} attention:\n\n${elementList}`
77+
}
78+
79+
if (finding.html) {
80+
return `An accessibility scan flagged the element \`${finding.html}\` ${reason}`
81+
}
82+
83+
return `An accessibility scan found an issue on ${finding.url} ${reason}`
84+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export const GROUP_BY_VALUES = ['finding', 'rule', 'rule+url'] as const
2+
3+
export type GroupBy = (typeof GROUP_BY_VALUES)[number]
4+
5+
export function isGroupBy(value: string): value is GroupBy {
6+
return (GROUP_BY_VALUES as readonly string[]).includes(value)
7+
}

0 commit comments

Comments
 (0)