name: Requirement Traceability Check on: push: branches: - master - main - develop pull_request: branches: - master - main - develop jobs: traceability: name: Validate Requirement Traces runs-on: [linux, amd64] steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup Bun uses: oven-sh/setup-bun@v1 with: bun-version: latest - name: Install dependencies run: bun install - name: Extract requirement traces run: bun run traces:json > traces.json - name: Validate trace format run: | if ! jq empty traces.json 2>/dev/null; then echo "❌ Invalid traces.json format" exit 1 fi echo "✅ Traces JSON is valid" - name: Check requirement coverage run: | set -e # Extract coverage stats TOTAL_TRACES=$(jq '.totalTraces' traces.json) UR_COUNT=$(jq '.byType.UR | length' traces.json) IR_COUNT=$(jq '.byType.IR | length' traces.json) DR_COUNT=$(jq '.byType.DR | length' traces.json) JA_COUNT=$(jq '.byType.JA | length' traces.json) echo "## 📊 Requirement Traceability Report" echo "" echo "**Total TRACES Found:** $TOTAL_TRACES" echo "" echo "### Requirements Covered:" echo "- User Requirements (UR): $UR_COUNT / 39 ($(( UR_COUNT * 100 / 39 ))%)" echo "- Integration Requirements (IR): $IR_COUNT / 24 ($(( IR_COUNT * 100 / 24 ))%)" echo "- Development Requirements (DR): $DR_COUNT / 48 ($(( DR_COUNT * 100 / 48 ))%)" echo "- Jellyfin API Requirements (JA): $JA_COUNT / 3 ($(( JA_COUNT * 100 / 3 ))%)" echo "" # Set minimum coverage threshold (50%) TOTAL_REQS=114 MIN_COVERAGE=$((TOTAL_REQS / 2)) COVERED=$((UR_COUNT + IR_COUNT + DR_COUNT + JA_COUNT)) COVERAGE_PERCENT=$((COVERED * 100 / TOTAL_REQS)) echo "**Overall Coverage:** $COVERED / $TOTAL_REQS ($COVERAGE_PERCENT%)" echo "" if [ "$COVERED" -lt "$MIN_COVERAGE" ]; then echo "❌ Coverage below minimum threshold ($COVERAGE_PERCENT% < 50%)" exit 1 else echo "✅ Coverage meets minimum threshold ($COVERAGE_PERCENT% >= 50%)" fi - name: Check for new untraced code run: | set -e # Find files modified in this PR/push if [ "${{ github.event_name }}" = "pull_request" ]; then CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD | grep -E '\.(ts|tsx|svelte|rs)$' || true) else CHANGED_FILES=$(git diff --name-only HEAD~1 | grep -E '\.(ts|tsx|svelte|rs)$' || true) fi if [ -z "$CHANGED_FILES" ]; then echo "✅ No source files changed" exit 0 fi echo "### Files Changed:" echo "$CHANGED_FILES" | sed 's/^/- /' echo "" # Check if changed files have TRACES UNTRACED_FILES="" while IFS= read -r file; do if [ -f "$file" ]; then # Skip test files and generated code if [[ "$file" == *".test."* ]] || [[ "$file" == *"node_modules"* ]]; then continue fi # Check if file has TRACES comments if ! grep -q "TRACES:" "$file" 2>/dev/null; then UNTRACED_FILES+="$file"$'\n' fi fi done <<< "$CHANGED_FILES" if [ -n "$UNTRACED_FILES" ]; then echo "⚠️ New files without TRACES:" echo "$UNTRACED_FILES" | sed 's/^/ - /' echo "" echo "💡 Add TRACES comments to link code to requirements:" echo " // TRACES: UR-001, UR-002 | DR-003" else echo "✅ All changed files have TRACES comments" fi - name: Generate traceability report if: always() run: bun run traces:markdown - name: Upload traceability report if: always() uses: actions/upload-artifact@v3 with: name: traceability-report path: docs/TRACEABILITY.md retention-days: 30 - name: Comment PR with coverage report if: github.event_name == 'pull_request' uses: actions/github-script@v7 with: script: | const fs = require('fs'); const traces = JSON.parse(fs.readFileSync('traces.json', 'utf8')); const urCount = traces.byType.UR.length; const irCount = traces.byType.IR.length; const drCount = traces.byType.DR.length; const jaCount = traces.byType.JA.length; const total = urCount + irCount + drCount + jaCount; const coverage = Math.round((total / 114) * 100); const comment = `## 📊 Requirement Traceability Report **Coverage:** ${coverage}% (${total}/114 requirements traced) ### By Type: - **User Requirements (UR):** ${urCount}/39 (${Math.round(urCount/39*100)}%) - **Integration Requirements (IR):** ${irCount}/24 (${Math.round(irCount/24*100)}%) - **Development Requirements (DR):** ${drCount}/48 (${Math.round(drCount/48*100)}%) - **Jellyfin API (JA):** ${jaCount}/3 (${Math.round(jaCount/3*100)}%) **Total Traces:** ${traces.totalTraces} [View full report](artifacts) | [Format Guide](https://github.com/yourusername/jellytau/blob/master/scripts/README.md#extract-tracests)`; github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: comment });