SFDAP A11y Audit (axe-core) #86
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # ============================================================ | |
| # SFDAP — Accessibility (axe-core) Audit | |
| # ============================================================ | |
| # Static dashboard SPA'sına axe-core WCAG 2.0 + 2.1 A/AA tarama uygular. | |
| # Strict mode: violation varsa job fail eder, gating görevi görür. | |
| # | |
| # Akış: | |
| # 1) Python + Node kurulumu | |
| # 2) FastAPI'yi arka planda başlat (port 8000) | |
| # 3) /dashboard/ erişilebilir olduğunu doğrula | |
| # 4) axe-core CLI ile WCAG 2.1 AA + WCAG 2.0 AA + WCAG 2.1 A tara | |
| # 5) İhlal özetini CI log'una bas | |
| # 6) Rapor artifact olarak yükle (30 gün retention) | |
| # | |
| # Tetikleyiciler: | |
| # - Pull request (main) | |
| # - Push (main) | |
| # - Haftalık cron (yeni WCAG kuralları + dependency güncellemesi yakalamak için) | |
| # - Manuel | |
| # ============================================================ | |
| name: SFDAP A11y Audit (axe-core) | |
| on: | |
| pull_request: | |
| branches: [main] | |
| push: | |
| branches: [main] | |
| schedule: | |
| - cron: "0 7 * * 1" # Mon 07:00 UTC | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| jobs: | |
| axe: | |
| name: ♿ axe-core WCAG 2.1 AA Tarama | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: 📥 Kodu indir | |
| uses: actions/checkout@v4 | |
| - name: 🐍 Python kur | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| cache: "pip" | |
| - name: 📦 Python bağımlılıklarını kur | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -r requirements.txt | |
| - name: 🟩 Node kur (axe-core/cli için) | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "20" | |
| cache: "npm" | |
| # Lockfile commit'te; setup-node hash bazlı cache yapar. | |
| # --- | |
| # Lockfile is committed; setup-node uses it for hash-keyed caching. | |
| cache-dependency-path: frontend/package-lock.json | |
| - name: 📦 axe-core CLI kur | |
| working-directory: frontend | |
| # `npm ci` deterministik bağımlılık ağacı kurar (package-lock.json | |
| # commit'te) — `npm install`'a göre daha hızlı ve hash-doğrulamalı. | |
| # --- | |
| # `npm ci` performs a deterministic install from the committed | |
| # package-lock.json — faster than `npm install` and verifies hashes. | |
| run: npm ci | |
| - name: 🚀 FastAPI sunucusunu arka planda başlat | |
| env: | |
| # Test API key — axe taraması auth-required değil (sadece /dashboard/ | |
| # statik route'unu tarıyor) ama config validation için set ederiz. | |
| API_KEY: "ci-test-key-not-real" | |
| SECRET_KEY: "ci-test-secret-not-real" | |
| ENVIRONMENT: "development" | |
| # MQTT broker yok — listener no-op | |
| MQTT_ENABLED: "false" | |
| run: | | |
| uvicorn app.main:app --host 127.0.0.1 --port 8000 & | |
| echo "FastAPI PID: $!" | |
| - name: ⏳ Sunucu hazır olana kadar bekle | |
| run: | | |
| for i in {1..30}; do | |
| if curl -sf http://127.0.0.1:8000/api/health > /dev/null; then | |
| echo "Server ready after ${i}s" | |
| break | |
| fi | |
| sleep 1 | |
| done | |
| # Dashboard erişilebilir mi? | |
| curl -sfI http://127.0.0.1:8000/dashboard/ | head -3 | |
| - name: 🔧 ChromeDriver'ı Chrome ile senkronize et | |
| # Runner imajındaki Chrome sürümü ile npx ile çekilen | |
| # @axe-core/cli'nin ChromeDriver'ı arada uyumsuz çıkıyor | |
| # ("This version of ChromeDriver only supports Chrome version N"). | |
| # browser-driver-manager runner'daki Chrome'a uyumlu driver yükler. | |
| run: npx --yes browser-driver-manager install chrome | |
| - name: ♿ axe-core taraması | |
| working-directory: frontend | |
| # `--exit` violation varsa exit 1 döner ve job fail eder. | |
| # `--timeout 60` saniye headless Chrome timeout'u. | |
| # WCAG tags: 2.0 A/AA + 2.1 A/AA — endüstri standardı gereksinim. | |
| # Çıktı: ../axe-report.json (artifact'a yüklenir). | |
| # --- | |
| # `--exit` returns 1 on violations; the job fails accordingly. | |
| # WCAG tags target both 2.0 and 2.1 A/AA — industry baseline. | |
| run: | | |
| npx --yes @axe-core/cli http://127.0.0.1:8000/dashboard/ \ | |
| --exit \ | |
| --timeout 60 \ | |
| --tags wcag2a,wcag2aa,wcag21a,wcag21aa \ | |
| --save ../axe-report.json | |
| - name: 📋 İhlal özetini logla | |
| # Strict mode'a geçtikten sonra başarısız run'larda hangi | |
| # kuralın patladığını CI log'unda doğrudan görmek için. | |
| # axe-report.json yüklendi mi diye `always()` ile koşar. | |
| # --- | |
| # Prints the violation summary directly into CI logs so a | |
| # failing strict run is debuggable without downloading the | |
| # JSON artifact. | |
| if: always() | |
| run: | | |
| if [ ! -f axe-report.json ]; then | |
| echo "axe-report.json yok — taramayı çalıştırma adımı başarısız olmuş olabilir." | |
| exit 0 | |
| fi | |
| python <<'PY' | |
| import json | |
| import sys | |
| from pathlib import Path | |
| data = json.loads(Path("axe-report.json").read_text()) | |
| # axe-cli sometimes emits a list (multi-URL) or a single dict. | |
| pages = data if isinstance(data, list) else [data] | |
| total = 0 | |
| for page in pages: | |
| violations = page.get("violations", []) | |
| if not violations: | |
| continue | |
| url = page.get("url", "(?)") | |
| print(f"❌ {url} — {len(violations)} ihlal kuralı") | |
| for v in violations: | |
| total += 1 | |
| nodes = len(v.get("nodes", [])) | |
| print(f" [{v.get('impact','?')}] {v['id']} — {v.get('help','')} ({nodes} düğüm)") | |
| for node in v.get("nodes", [])[:2]: | |
| target = node.get("target", ["?"]) | |
| print(f" target: {target}") | |
| if total == 0: | |
| print("✅ axe-core: WCAG 2.0/2.1 A+AA için sıfır ihlal.") | |
| else: | |
| print(f"\nToplam {total} ihlal kuralı (strict mode → job fail).") | |
| sys.exit(0) # actual fail comes from the previous axe step | |
| PY | |
| - name: 📊 axe-core rapor artifact | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: axe-report | |
| path: axe-report.json | |
| retention-days: 30 |