@mcptoolshop/accessibility-suite 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.a11y_artifacts_test/evidence.json +52 -0
- package/.a11y_artifacts_test/gate-result.json +41 -0
- package/.a11y_artifacts_test/report.txt +19 -0
- package/.github/actions/a11y-ci/action.yml +106 -0
- package/.github/workflows/a11y-gate.yml +112 -0
- package/.github/workflows/ci.yml +68 -3
- package/.github/workflows/test-a11y-action.yml +93 -0
- package/.github/workflows/update-baseline.yml +49 -0
- package/.github/workflows/verify-docs.yml +26 -0
- package/CHANGELOG.md +33 -0
- package/GETTING_STARTED.md +87 -0
- package/HANDBOOK.md +747 -0
- package/README.md +202 -23
- package/assets/a11y-logo.png +0 -0
- package/docs/handbooks/A11Y-ASSIST.md +31 -0
- package/docs/handbooks/A11Y-CI.md +71 -0
- package/docs/handbooks/A11Y-DEMO-SITE.md +29 -0
- package/docs/handbooks/A11Y-EVIDENCE-ENGINE.md +31 -0
- package/docs/handbooks/A11Y-LINT.md +62 -0
- package/docs/handbooks/A11Y-MCP-TOOLS.md +34 -0
- package/docs/handbooks/ACCESSIBILITY-SUITE.md +51 -0
- package/docs/handbooks/ALLY-DEMO-PYTHON.md +23 -0
- package/docs/handbooks/COMMON-CONCEPTS.md +24 -0
- package/docs/handbooks/CURSORASSIST.md +18 -0
- package/docs/handbooks/README.md +20 -0
- package/docs/prov-spec/SETUP.md +1 -1
- package/docs/rules.md +132 -0
- package/docs/unified-artifacts.md +52 -0
- package/logo.png +0 -0
- package/package.json +1 -1
- package/pipelines/templates/a11y-ci.yml +135 -0
- package/pipelines/test-a11y-ci-template.yml +36 -0
- package/scripts/verify_handbooks.py +97 -0
- package/src/a11y-assist/README.md +5 -0
- package/src/a11y-ci/.a11y_artifacts_test/current.scorecard.json +11 -0
- package/src/a11y-ci/.a11y_artifacts_test/evidence.json +52 -0
- package/src/a11y-ci/.a11y_artifacts_test/gate-result.json +41 -0
- package/src/a11y-ci/.a11y_artifacts_test/report.txt +19 -0
- package/src/a11y-ci/README.md +83 -23
- package/src/a11y-ci/a11y_ci/allowlist.py +52 -9
- package/src/a11y-ci/a11y_ci/cli.py +143 -46
- package/src/a11y-ci/a11y_ci/error_ids.py +17 -0
- package/src/a11y-ci/a11y_ci/gate.py +83 -48
- package/src/a11y-ci/a11y_ci/help.py +119 -0
- package/src/a11y-ci/a11y_ci/mcp_payload.py +124 -0
- package/src/a11y-ci/a11y_ci/pr_comment.py +127 -0
- package/src/a11y-ci/a11y_ci/report.py +137 -0
- package/src/a11y-ci/a11y_ci/schema/scorecard.schema.json +89 -0
- package/src/a11y-ci/a11y_ci/schemas/allowlist.schema.json +11 -2
- package/src/a11y-ci/a11y_ci/scorecard.py +86 -30
- package/src/a11y-ci/a11y_ci/severity.py +29 -0
- package/src/a11y-ci/npm/README.md +47 -0
- package/src/a11y-ci/npm/package.json +1 -1
- package/src/a11y-ci/tests/fixtures/allowlist_expired.json +2 -1
- package/src/a11y-ci/tests/fixtures/allowlist_ok.json +2 -1
- package/src/a11y-ci/tests/fixtures/baseline_ok.json +17 -4
- package/src/a11y-ci/tests/fixtures/current_fail.json +10 -3
- package/src/a11y-ci/tests/fixtures/current_failures_many.json +11 -0
- package/src/a11y-ci/tests/fixtures/current_ok.json +10 -3
- package/src/a11y-ci/tests/fixtures/current_regresses.json +15 -4
- package/src/a11y-ci/tests/test_allowlist_v2.py +97 -0
- package/src/a11y-ci/tests/test_gate.py +3 -3
- package/src/a11y-ci/tests/test_mcp_cli.py +80 -0
- package/src/a11y-ci/tests/test_mcp_payload.py +76 -0
- package/src/a11y-ci/tests/test_polish.py +83 -0
- package/src/a11y-ci/tests/test_pr_comment.py +103 -0
- package/src/a11y-ci/tests/test_rule_help.py +70 -0
- package/src/a11y-ci/tests/test_schema_validation.py +36 -0
- package/src/a11y-ci/tests/test_scorecard_canonical.py +88 -0
- package/src/a11y-ci/tests/test_smoke_cli.py +41 -0
- package/src/a11y-evidence-engine/README.md +5 -0
- package/src/a11y-lint/README.md +5 -0
- package/src/a11y-lint/a11y_lint/cli.py +29 -0
- package/src/a11y-mcp-tools/README.md +5 -0
- package/tools/ado/a11y-ci.ps1 +195 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"tool": "a11y-ci",
|
|
3
|
+
"tool_version": "0.1.0",
|
|
4
|
+
"run_id": "56a4a7ea-f8e8-40a9-a27b-aec92f9c96aa",
|
|
5
|
+
"timestamp": "2026-02-15T02:15:35.804358+00:00",
|
|
6
|
+
"repo": null,
|
|
7
|
+
"commit_sha": null,
|
|
8
|
+
"workflow": null,
|
|
9
|
+
"gate": {
|
|
10
|
+
"decision": "fail",
|
|
11
|
+
"exit_code": 3,
|
|
12
|
+
"fail_on": "serious",
|
|
13
|
+
"counts": {
|
|
14
|
+
"info": 0,
|
|
15
|
+
"minor": 0,
|
|
16
|
+
"moderate": 0,
|
|
17
|
+
"serious": 3,
|
|
18
|
+
"critical": 0
|
|
19
|
+
},
|
|
20
|
+
"deltas": {}
|
|
21
|
+
},
|
|
22
|
+
"blocking": [
|
|
23
|
+
{
|
|
24
|
+
"id": "TEST.001",
|
|
25
|
+
"fingerprint": "7c61e0b75cae921316221a4e5c756285e3e8837086df60a627332fbdf53c7196",
|
|
26
|
+
"severity": "serious",
|
|
27
|
+
"message": "msg",
|
|
28
|
+
"location": null
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"id": "TEST.002",
|
|
32
|
+
"fingerprint": "ed70b4e75a112074812a72be997ffe074ff9d0eec48fe4b0a7d00fc15d546782",
|
|
33
|
+
"severity": "serious",
|
|
34
|
+
"message": "msg",
|
|
35
|
+
"location": null
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"id": "TEST.003",
|
|
39
|
+
"fingerprint": "1fb34777b302157e998e8db92c9be20c9e1316649c2edfb825151dcaca45df56",
|
|
40
|
+
"severity": "serious",
|
|
41
|
+
"message": "msg",
|
|
42
|
+
"location": null
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"artifacts": [
|
|
46
|
+
{
|
|
47
|
+
"kind": "scorecard",
|
|
48
|
+
"path": "tests/fixtures/current_failures_many.json",
|
|
49
|
+
"sha256": "93d6c211476cb614b30e30256c9644d7b7694d0a67ca72bfc0a872cb4fb57196"
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"gate": "FAIL",
|
|
3
|
+
"timestamp": "ISO8601-TODO",
|
|
4
|
+
"counts": {
|
|
5
|
+
"info": 0,
|
|
6
|
+
"minor": 0,
|
|
7
|
+
"moderate": 0,
|
|
8
|
+
"serious": 3,
|
|
9
|
+
"critical": 0
|
|
10
|
+
},
|
|
11
|
+
"baseline_counts": null,
|
|
12
|
+
"reasons": [
|
|
13
|
+
"Current run has 3 finding(s) at or above 'serious'."
|
|
14
|
+
],
|
|
15
|
+
"blocking": {
|
|
16
|
+
"current_ids": [
|
|
17
|
+
"TEST.001",
|
|
18
|
+
"TEST.002",
|
|
19
|
+
"TEST.003"
|
|
20
|
+
],
|
|
21
|
+
"details": [
|
|
22
|
+
{
|
|
23
|
+
"id": "TEST.001",
|
|
24
|
+
"help_url": null,
|
|
25
|
+
"help_hint": null
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": "TEST.002",
|
|
29
|
+
"help_url": null,
|
|
30
|
+
"help_hint": null
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"id": "TEST.003",
|
|
34
|
+
"help_url": null,
|
|
35
|
+
"help_hint": null
|
|
36
|
+
}
|
|
37
|
+
],
|
|
38
|
+
"new_ids": [],
|
|
39
|
+
"new_fingerprints": []
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
[ERROR] Accessibility gate failed (ID: A11Y.CI.GATE.FAIL)
|
|
2
|
+
|
|
3
|
+
What:
|
|
4
|
+
Accessibility policy violations were detected.
|
|
5
|
+
|
|
6
|
+
Summary:
|
|
7
|
+
Serious: 3
|
|
8
|
+
|
|
9
|
+
Why:
|
|
10
|
+
Current run has 3 finding(s) at or above 'serious'.
|
|
11
|
+
|
|
12
|
+
Fix:
|
|
13
|
+
Address the listed findings or update the baseline.
|
|
14
|
+
Run local check: a11y-ci gate --current <path>
|
|
15
|
+
|
|
16
|
+
Blocking IDs (Top 10):
|
|
17
|
+
- TEST.001
|
|
18
|
+
- TEST.002
|
|
19
|
+
- TEST.003
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
name: 'Accessibility Gate (a11y-ci)'
|
|
2
|
+
description: 'Runs a11y-ci policy gate with unified artifact handling.'
|
|
3
|
+
inputs:
|
|
4
|
+
artifact-dir:
|
|
5
|
+
description: 'Directory to store artifacts'
|
|
6
|
+
required: false
|
|
7
|
+
default: '.a11y_artifacts'
|
|
8
|
+
current:
|
|
9
|
+
description: 'Path to current scorecard JSON (optional if in artifact-dir)'
|
|
10
|
+
required: false
|
|
11
|
+
baseline:
|
|
12
|
+
description: 'Path to baseline scorecard JSON (optional if in artifact-dir)'
|
|
13
|
+
required: false
|
|
14
|
+
allowlist:
|
|
15
|
+
description: 'Path to allowlist JSON (optional if in artifact-dir)'
|
|
16
|
+
required: false
|
|
17
|
+
fail-on:
|
|
18
|
+
description: 'Minimum severity to fail on (info, minor, moderate, serious, critical)'
|
|
19
|
+
required: false
|
|
20
|
+
default: 'serious'
|
|
21
|
+
top:
|
|
22
|
+
description: 'Limit N blocking findings in output'
|
|
23
|
+
required: false
|
|
24
|
+
default: '10'
|
|
25
|
+
platform:
|
|
26
|
+
description: 'Markdown flavor for PR comments (github or ado)'
|
|
27
|
+
required: false
|
|
28
|
+
default: 'github'
|
|
29
|
+
post-comment:
|
|
30
|
+
description: 'Whether to prepare a PR comment file'
|
|
31
|
+
required: false
|
|
32
|
+
default: 'true'
|
|
33
|
+
|
|
34
|
+
outputs:
|
|
35
|
+
exit_code:
|
|
36
|
+
description: 'Exit code of the gate'
|
|
37
|
+
value: ${{ steps.gate.outputs.exit_code }}
|
|
38
|
+
evidence_path:
|
|
39
|
+
description: 'Path to the generated evidence JSON'
|
|
40
|
+
value: ${{ steps.gate.outputs.evidence_path }}
|
|
41
|
+
comment_path:
|
|
42
|
+
description: 'Path to the generated PR comment markdown'
|
|
43
|
+
value: ${{ steps.gate.outputs.comment_path }}
|
|
44
|
+
gate_result_path:
|
|
45
|
+
description: 'Path to the generated gate result JSON'
|
|
46
|
+
value: ${{ steps.gate.outputs.gate_result_path }}
|
|
47
|
+
|
|
48
|
+
runs:
|
|
49
|
+
using: "composite"
|
|
50
|
+
steps:
|
|
51
|
+
- name: Run Accessibility Gate
|
|
52
|
+
id: gate
|
|
53
|
+
shell: bash
|
|
54
|
+
run: |
|
|
55
|
+
# 1. Setup Artifacts
|
|
56
|
+
ARTIFACT_DIR="${{ inputs.artifact-dir }}"
|
|
57
|
+
mkdir -p "$ARTIFACT_DIR"
|
|
58
|
+
echo "Using artifact directory: $ARTIFACT_DIR"
|
|
59
|
+
|
|
60
|
+
# 2. Construct Arguments
|
|
61
|
+
ARGS="gate --artifact-dir $ARTIFACT_DIR"
|
|
62
|
+
|
|
63
|
+
if [ -n "${{ inputs.current }}" ]; then
|
|
64
|
+
ARGS="$ARGS --current ${{ inputs.current }}"
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
if [ -n "${{ inputs.baseline }}" ]; then
|
|
68
|
+
ARGS="$ARGS --baseline ${{ inputs.baseline }}"
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
if [ -n "${{ inputs.allowlist }}" ]; then
|
|
72
|
+
ARGS="$ARGS --allowlist ${{ inputs.allowlist }}"
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
ARGS="$ARGS --fail-on ${{ inputs.fail-on }}"
|
|
76
|
+
ARGS="$ARGS --top ${{ inputs.top }}"
|
|
77
|
+
|
|
78
|
+
# 3. Run Gate
|
|
79
|
+
echo "Running: a11y-ci $ARGS"
|
|
80
|
+
|
|
81
|
+
# Run and capture exit code, but don't fail immediately so we can process artifacts
|
|
82
|
+
set +e
|
|
83
|
+
a11y-ci $ARGS
|
|
84
|
+
EXIT_CODE=$?
|
|
85
|
+
set -e
|
|
86
|
+
|
|
87
|
+
echo "Gate Exit Code: $EXIT_CODE"
|
|
88
|
+
echo "exit_code=$EXIT_CODE" >> $GITHUB_OUTPUT
|
|
89
|
+
|
|
90
|
+
# 4. Generate Comment (if requested and evidence exists)
|
|
91
|
+
EVIDENCE_JSON="$ARTIFACT_DIR/evidence.json"
|
|
92
|
+
COMMENT_MD="$ARTIFACT_DIR/comment.md"
|
|
93
|
+
GATE_RESULT="$ARTIFACT_DIR/gate-result.json"
|
|
94
|
+
|
|
95
|
+
if [ "${{ inputs.post-comment }}" = "true" ] && [ -f "$EVIDENCE_JSON" ]; then
|
|
96
|
+
echo "Generating PR Comment..."
|
|
97
|
+
a11y-ci comment --mcp "$EVIDENCE_JSON" --platform "${{ inputs.platform }}" --top "${{ inputs.top }}" > "$COMMENT_MD"
|
|
98
|
+
echo "comment_path=$COMMENT_MD" >> $GITHUB_OUTPUT
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
# 5. Set Outputs
|
|
102
|
+
echo "evidence_path=$EVIDENCE_JSON" >> $GITHUB_OUTPUT
|
|
103
|
+
echo "gate_result_path=$GATE_RESULT" >> $GITHUB_OUTPUT
|
|
104
|
+
|
|
105
|
+
# 6. Exit with code
|
|
106
|
+
exit $EXIT_CODE
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
name: Accessibility Gate
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
paths:
|
|
7
|
+
- '**/*.md'
|
|
8
|
+
- '**/*.html'
|
|
9
|
+
- 'src/**'
|
|
10
|
+
pull_request:
|
|
11
|
+
branches: [main]
|
|
12
|
+
paths:
|
|
13
|
+
- '**/*.md'
|
|
14
|
+
- '**/*.html'
|
|
15
|
+
- 'src/**'
|
|
16
|
+
workflow_dispatch:
|
|
17
|
+
|
|
18
|
+
permissions:
|
|
19
|
+
contents: read
|
|
20
|
+
checks: write
|
|
21
|
+
pull-requests: write # For posting comments (later step)
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
accessibility-gate:
|
|
25
|
+
name: Accessibility Compliance
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
steps:
|
|
28
|
+
- name: Checkout Code
|
|
29
|
+
uses: actions/checkout@v4
|
|
30
|
+
|
|
31
|
+
- name: Setup Python
|
|
32
|
+
uses: actions/setup-python@v5
|
|
33
|
+
with:
|
|
34
|
+
python-version: '3.11'
|
|
35
|
+
|
|
36
|
+
# In a real repo, you'd pip install a11y-lint a11y-ci
|
|
37
|
+
# Here we install from local source for dogfooding
|
|
38
|
+
- name: Install Tools (Local)
|
|
39
|
+
run: |
|
|
40
|
+
pip install ./src/a11y-lint
|
|
41
|
+
pip install ./src/a11y-ci
|
|
42
|
+
|
|
43
|
+
- name: Generate Scorecard (Scan)
|
|
44
|
+
run: |
|
|
45
|
+
mkdir -p .a11y_artifacts
|
|
46
|
+
# Scanning docs for demonstration
|
|
47
|
+
# Uses --artifact-dir to automatically write current.scorecard.json
|
|
48
|
+
a11y-lint scan docs --artifact-dir .a11y_artifacts
|
|
49
|
+
echo "Generated scorecard in .a11y_artifacts/"
|
|
50
|
+
|
|
51
|
+
# The reusable Action
|
|
52
|
+
- name: Run Accessibility Gate
|
|
53
|
+
id: gate
|
|
54
|
+
uses: ./.github/actions/a11y-ci
|
|
55
|
+
with:
|
|
56
|
+
artifact-dir: .a11y_artifacts
|
|
57
|
+
baseline: docs/baselines/a11y.scorecard.json
|
|
58
|
+
fail-on: serious
|
|
59
|
+
platform: github
|
|
60
|
+
|
|
61
|
+
- name: Post PR Comment
|
|
62
|
+
if: github.event_name == 'pull_request' && (success() || failure())
|
|
63
|
+
uses: actions/github-script@v7
|
|
64
|
+
with:
|
|
65
|
+
script: |
|
|
66
|
+
const fs = require('fs');
|
|
67
|
+
// Read the generated comment file
|
|
68
|
+
const commentPath = '${{ steps.gate.outputs.comment_path }}';
|
|
69
|
+
if (!commentPath || !fs.existsSync(commentPath)) {
|
|
70
|
+
console.log('No comment file to post.');
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const body = fs.readFileSync(commentPath, 'utf8');
|
|
74
|
+
const marker = '<!-- a11y-ci-sticky-comment -->';
|
|
75
|
+
const fullBody = `${marker}\n${body}`;
|
|
76
|
+
|
|
77
|
+
const { owner, repo } = context.repo;
|
|
78
|
+
const issue_number = context.issue.number;
|
|
79
|
+
|
|
80
|
+
// Find existing comment
|
|
81
|
+
const comments = await github.rest.issues.listComments({
|
|
82
|
+
owner,
|
|
83
|
+
repo,
|
|
84
|
+
issue_number,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const existing = comments.data.find(c => c.body.includes(marker));
|
|
88
|
+
|
|
89
|
+
if (existing) {
|
|
90
|
+
await github.rest.issues.updateComment({
|
|
91
|
+
owner,
|
|
92
|
+
repo,
|
|
93
|
+
comment_id: existing.id,
|
|
94
|
+
body: fullBody,
|
|
95
|
+
});
|
|
96
|
+
console.log(`Updated comment ${existing.id}`);
|
|
97
|
+
} else {
|
|
98
|
+
await github.rest.issues.createComment({
|
|
99
|
+
owner,
|
|
100
|
+
repo,
|
|
101
|
+
issue_number,
|
|
102
|
+
body: fullBody,
|
|
103
|
+
});
|
|
104
|
+
console.log('Created new comment');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
- name: Upload Artifacts
|
|
108
|
+
if: always()
|
|
109
|
+
uses: actions/upload-artifact@v4
|
|
110
|
+
with:
|
|
111
|
+
name: accessibility-artifacts
|
|
112
|
+
path: .a11y_artifacts/**
|
package/.github/workflows/ci.yml
CHANGED
|
@@ -6,6 +6,10 @@ on:
|
|
|
6
6
|
pull_request:
|
|
7
7
|
branches: [main]
|
|
8
8
|
|
|
9
|
+
concurrency:
|
|
10
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
9
13
|
jobs:
|
|
10
14
|
# Python projects: a11y-assist, a11y-ci, a11y-lint
|
|
11
15
|
python:
|
|
@@ -25,6 +29,7 @@ jobs:
|
|
|
25
29
|
uses: actions/setup-python@v5
|
|
26
30
|
with:
|
|
27
31
|
python-version: '3.11'
|
|
32
|
+
cache: 'pip'
|
|
28
33
|
|
|
29
34
|
- name: Install dependencies
|
|
30
35
|
working-directory: src/${{ matrix.project }}
|
|
@@ -34,7 +39,18 @@ jobs:
|
|
|
34
39
|
|
|
35
40
|
- name: Run tests
|
|
36
41
|
working-directory: src/${{ matrix.project }}
|
|
37
|
-
|
|
42
|
+
# Check if tests exist (exit code 5 = no tests). If they exist, run them and fail on error.
|
|
43
|
+
run: |
|
|
44
|
+
pytest --collect-only > /dev/null 2>&1
|
|
45
|
+
EXIT_CODE=$?
|
|
46
|
+
if [ $EXIT_CODE -eq 5 ]; then
|
|
47
|
+
echo "::warning::No tests found for ${{ matrix.project }}."
|
|
48
|
+
exit 0
|
|
49
|
+
elif [ $EXIT_CODE -ne 0 ]; then
|
|
50
|
+
echo "::error::Error collecting tests."
|
|
51
|
+
exit 1
|
|
52
|
+
fi
|
|
53
|
+
pytest -v
|
|
38
54
|
|
|
39
55
|
# Node.js projects: a11y-evidence-engine, a11y-mcp-tools
|
|
40
56
|
nodejs:
|
|
@@ -53,11 +69,60 @@ jobs:
|
|
|
53
69
|
uses: actions/setup-node@v4
|
|
54
70
|
with:
|
|
55
71
|
node-version: '20'
|
|
72
|
+
cache: 'npm'
|
|
56
73
|
|
|
57
74
|
- name: Install dependencies
|
|
58
75
|
working-directory: src/${{ matrix.project }}
|
|
59
76
|
run: npm ci
|
|
60
77
|
|
|
61
|
-
- name: Run tests
|
|
78
|
+
- name: Run tests (CI Must Fail)
|
|
62
79
|
working-directory: src/${{ matrix.project }}
|
|
63
|
-
|
|
80
|
+
# If npm test script exits non-zero, fail the build.
|
|
81
|
+
# This prevents accidental regressions from being swallowed.
|
|
82
|
+
run: npm test
|
|
83
|
+
|
|
84
|
+
# Integration fixture verification for a11y-ci
|
|
85
|
+
gate-verification:
|
|
86
|
+
runs-on: ubuntu-latest
|
|
87
|
+
steps:
|
|
88
|
+
- uses: actions/checkout@v4
|
|
89
|
+
- uses: actions/setup-python@v5
|
|
90
|
+
with:
|
|
91
|
+
python-version: '3.11'
|
|
92
|
+
cache: 'pip'
|
|
93
|
+
|
|
94
|
+
- name: Install a11y-ci
|
|
95
|
+
working-directory: src/a11y-ci
|
|
96
|
+
run: pip install .
|
|
97
|
+
|
|
98
|
+
- name: Gate Pass Check
|
|
99
|
+
working-directory: src/a11y-ci
|
|
100
|
+
run: a11y-ci gate --current tests/fixtures/current_ok.json
|
|
101
|
+
|
|
102
|
+
- name: Gate Fail Check (JSON Report)
|
|
103
|
+
id: fail_check
|
|
104
|
+
working-directory: src/a11y-ci
|
|
105
|
+
# We expect failure (exit code 3), so we use || true to prevent job failure,
|
|
106
|
+
# but capture output. In a real pipeline, we'd want to fail if it DOESN'T fail.
|
|
107
|
+
run: |
|
|
108
|
+
set +e
|
|
109
|
+
a11y-ci gate --current tests/fixtures/current_fail.json --format json > report.json
|
|
110
|
+
EXIT_CODE=$?
|
|
111
|
+
set -e
|
|
112
|
+
|
|
113
|
+
echo "Exit Code: $EXIT_CODE"
|
|
114
|
+
if [ $EXIT_CODE -ne 3 ]; then
|
|
115
|
+
echo "::error::Expected exit code 3 for partial failure, got $EXIT_CODE"
|
|
116
|
+
exit 1
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
echo "::group::JSON Report"
|
|
120
|
+
cat report.json
|
|
121
|
+
echo "::endgroup::"
|
|
122
|
+
|
|
123
|
+
- name: Upload Gate Artifacts
|
|
124
|
+
if: always()
|
|
125
|
+
uses: actions/upload-artifact@v4
|
|
126
|
+
with:
|
|
127
|
+
name: a11y-gate-report
|
|
128
|
+
path: src/a11y-ci/report.json
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
name: Test Accessibility Action (Integration)
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
paths:
|
|
7
|
+
- '.github/actions/a11y-ci/**'
|
|
8
|
+
- '.github/workflows/test-a11y-action.yml'
|
|
9
|
+
pull_request:
|
|
10
|
+
branches: [main]
|
|
11
|
+
paths:
|
|
12
|
+
- '.github/actions/a11y-ci/**'
|
|
13
|
+
- '.github/workflows/test-a11y-action.yml'
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
test-pass:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
- uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: '3.11'
|
|
23
|
+
|
|
24
|
+
- name: Install a11y-ci
|
|
25
|
+
run: pip install ./src/a11y-ci
|
|
26
|
+
|
|
27
|
+
- name: Create Passing Scorecard
|
|
28
|
+
run: |
|
|
29
|
+
echo '{"meta": {"tool": "test", "version": "1"}, "findings": []}' > pass.json
|
|
30
|
+
|
|
31
|
+
- name: Run Action (Pass)
|
|
32
|
+
id: gate
|
|
33
|
+
uses: ./.github/actions/a11y-ci
|
|
34
|
+
with:
|
|
35
|
+
current: pass.json
|
|
36
|
+
fail-on: serious
|
|
37
|
+
# Use console output for testing
|
|
38
|
+
post-comment: true
|
|
39
|
+
|
|
40
|
+
- name: Assert Outcomes
|
|
41
|
+
if: steps.gate.outputs.exit_code != '0'
|
|
42
|
+
run: |
|
|
43
|
+
echo "Expected exit code 0, got ${{ steps.gate.outputs.exit_code }}"
|
|
44
|
+
exit 1
|
|
45
|
+
|
|
46
|
+
- name: Assert Artifacts
|
|
47
|
+
run: |
|
|
48
|
+
if [ ! -f "${{ steps.gate.outputs.report_json_path }}" ]; then exit 1; fi
|
|
49
|
+
if [ ! -f "${{ steps.gate.outputs.mcp_payload_path }}" ]; then exit 1; fi
|
|
50
|
+
if [ ! -f "${{ steps.gate.outputs.comment_md_path }}" ]; then exit 1; fi
|
|
51
|
+
|
|
52
|
+
# Check comment content
|
|
53
|
+
if ! grep -q "Accessibility Gate: PASS" "${{ steps.gate.outputs.comment_md_path }}"; then
|
|
54
|
+
echo "Comment does not indicate PASS"
|
|
55
|
+
cat "${{ steps.gate.outputs.comment_md_path }}"
|
|
56
|
+
exit 1
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
test-fail:
|
|
60
|
+
runs-on: ubuntu-latest
|
|
61
|
+
steps:
|
|
62
|
+
- uses: actions/checkout@v4
|
|
63
|
+
- uses: actions/setup-python@v5
|
|
64
|
+
with:
|
|
65
|
+
python-version: '3.11'
|
|
66
|
+
|
|
67
|
+
- name: Install a11y-ci
|
|
68
|
+
run: pip install ./src/a11y-ci
|
|
69
|
+
|
|
70
|
+
- name: Create Failing Scorecard
|
|
71
|
+
run: |
|
|
72
|
+
echo '{"meta": {"tool": "test", "version": "1"}, "findings": [{"id": "FAIL", "severity": "serious", "message": "fail"}]}' > fail.json
|
|
73
|
+
|
|
74
|
+
- name: Run Action (Fail)
|
|
75
|
+
id: gate
|
|
76
|
+
continue-on-error: true
|
|
77
|
+
uses: ./.github/actions/a11y-ci
|
|
78
|
+
with:
|
|
79
|
+
current: fail.json
|
|
80
|
+
fail-on: serious
|
|
81
|
+
|
|
82
|
+
- name: Assert Outcomes
|
|
83
|
+
if: steps.gate.outputs.exit_code != '3'
|
|
84
|
+
run: |
|
|
85
|
+
echo "Expected exit code 3, got ${{ steps.gate.outputs.exit_code }}"
|
|
86
|
+
exit 1
|
|
87
|
+
|
|
88
|
+
- name: Assert Comment Content
|
|
89
|
+
run: |
|
|
90
|
+
if ! grep -q "Accessibility Gate: FAIL" "${{ steps.gate.outputs.comment_md_path }}"; then
|
|
91
|
+
echo "Comment does not indicate FAIL"
|
|
92
|
+
exit 1
|
|
93
|
+
fi
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
name: Update Baseline
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
reason:
|
|
7
|
+
description: 'Reason for updating baseline (e.g., new feature, temporary failure)'
|
|
8
|
+
required: true
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: write
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
update-baseline:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- name: Checkout Code
|
|
18
|
+
uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Setup Python
|
|
21
|
+
uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: '3.11'
|
|
24
|
+
|
|
25
|
+
# Install Tools
|
|
26
|
+
- name: Install Tools (Local)
|
|
27
|
+
run: |
|
|
28
|
+
pip install ./src/a11y-lint
|
|
29
|
+
pip install ./src/a11y-ci
|
|
30
|
+
|
|
31
|
+
# Scan
|
|
32
|
+
- name: Generate Scorecard (Scan)
|
|
33
|
+
run: |
|
|
34
|
+
a11y-lint scan docs --format json > docs/baselines/a11y.scorecard.json
|
|
35
|
+
|
|
36
|
+
# Detect changes and commit
|
|
37
|
+
- name: Commit Baseline
|
|
38
|
+
run: |
|
|
39
|
+
git config --global user.name 'a11y-ci-bot'
|
|
40
|
+
git config --global user.email 'a11y-ci-bot@users.noreply.github.com'
|
|
41
|
+
|
|
42
|
+
if git diff --quiet docs/baselines/a11y.scorecard.json; then
|
|
43
|
+
echo "No changes in baseline."
|
|
44
|
+
else
|
|
45
|
+
git add docs/baselines/a11y.scorecard.json
|
|
46
|
+
git commit -m "chore(baseline): update a11y baseline" -m "${{ github.event.inputs.reason }}"
|
|
47
|
+
git push
|
|
48
|
+
echo "Baseline updated."
|
|
49
|
+
fi
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: Verify Documentation
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
paths:
|
|
6
|
+
- 'docs/handbooks/**'
|
|
7
|
+
- 'scripts/verify_handbooks.py'
|
|
8
|
+
pull_request:
|
|
9
|
+
paths:
|
|
10
|
+
- 'docs/handbooks/**'
|
|
11
|
+
- 'scripts/verify_handbooks.py'
|
|
12
|
+
workflow_dispatch:
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
verify-handbooks:
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Set up Python
|
|
21
|
+
uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: '3.11'
|
|
24
|
+
|
|
25
|
+
- name: Verify Handbooks
|
|
26
|
+
run: python scripts/verify_handbooks.py
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.2.0] - 2026-02-17
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Product-focused README.md replacing the migration notice with suite value proposition, architecture overview, project table, quick start guides, MCP client configuration, and documentation links.
|
|
13
|
+
- HANDBOOK.md with comprehensive documentation covering the accessibility testing problem space, architecture deep dives for all six sub-projects, evidence-based testing with prov-spec provenance, CI integration patterns (GitHub Actions and Azure DevOps), MCP integration guide, development setup for the multi-language monorepo, and FAQ.
|
|
14
|
+
- CHANGELOG.md in Keep a Changelog format.
|
|
15
|
+
- MIT license badge in README.md.
|
|
16
|
+
|
|
17
|
+
## [0.1.0] - 2026-02-12
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- Initial monorepo structure consolidating six accessibility tools.
|
|
22
|
+
- a11y-lint: accessibility linter for CLI output with WCAG and policy rules.
|
|
23
|
+
- a11y-ci: CI gate for accessibility scorecards with regression detection and allowlists.
|
|
24
|
+
- a11y-assist: low-vision-first CLI assistant with five accessibility profiles (lowvision, cognitive-load, screen-reader, dyslexia, plain-language).
|
|
25
|
+
- a11y-evidence-engine: headless HTML scanner with prov-spec provenance records.
|
|
26
|
+
- a11y-mcp-tools: MCP server exposing evidence capture and diagnosis tools.
|
|
27
|
+
- a11y-demo-site: demo site with intentional violations for end-to-end testing.
|
|
28
|
+
- Unified artifact strategy with `.a11y_artifacts/` directory convention.
|
|
29
|
+
- GETTING_STARTED.md with three-command local setup and CI templates.
|
|
30
|
+
- prov-spec documentation in `docs/prov-spec/`.
|
|
31
|
+
|
|
32
|
+
[0.2.0]: https://github.com/mcp-tool-shop-org/accessibility-suite/compare/v0.1.0...v0.2.0
|
|
33
|
+
[0.1.0]: https://github.com/mcp-tool-shop-org/accessibility-suite/releases/tag/v0.1.0
|