feat: add Kotlin language support for codebase analysis (#287) #173
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
| # Security Note: This workflow uses workflow_dispatch inputs and pull_request events. | ||
| # All untrusted inputs are accessed via environment variables (env:) as recommended. | ||
| # No direct usage of github.event.issue/comment/review content in run: commands. | ||
| name: Quality Metrics Dashboard | ||
| on: | ||
| workflow_dispatch: | ||
| inputs: | ||
| skill_dir: | ||
| description: 'Path to skill directory to analyze (e.g., output/react)' | ||
| required: true | ||
| type: string | ||
| fail_threshold: | ||
| description: 'Minimum quality score to pass (default: 70)' | ||
| required: false | ||
| default: '70' | ||
| type: string | ||
| pull_request: | ||
| paths: | ||
| - 'output/**' | ||
| - 'configs/**' | ||
| jobs: | ||
| analyze: | ||
| name: Quality Metrics Analysis | ||
| runs-on: ubuntu-latest | ||
| env: | ||
| SKILL_DIR_INPUT: ${{ github.event.inputs.skill_dir }} | ||
| FAIL_THRESHOLD_INPUT: ${{ github.event.inputs.fail_threshold }} | ||
| steps: | ||
| - uses: actions/checkout@v3 | ||
| - name: Set up Python 3.12 | ||
| uses: actions/setup-python@v4 | ||
| with: | ||
| python-version: '3.12' | ||
| - name: Install dependencies | ||
| run: | | ||
| python -m pip install --upgrade pip | ||
| pip install -e . | ||
| - name: Find skill directories | ||
| id: find_skills | ||
| run: | | ||
| if [ -n "$SKILL_DIR_INPUT" ]; then | ||
| # Manual trigger with specific directory | ||
| echo "dirs=$SKILL_DIR_INPUT" >> $GITHUB_OUTPUT | ||
| else | ||
| # PR trigger - find all skill directories | ||
| DIRS=$(find output -maxdepth 1 -type d -name "*" ! -name "output" | tr '\n' ' ' || echo "") | ||
| if [ -z "$DIRS" ]; then | ||
| echo "No skill directories found" | ||
| echo "dirs=" >> $GITHUB_OUTPUT | ||
| else | ||
| echo "dirs=$DIRS" >> $GITHUB_OUTPUT | ||
| fi | ||
| fi | ||
| - name: Analyze quality metrics | ||
| id: quality | ||
| run: | | ||
| DIRS="${{ steps.find_skills.outputs.dirs }}" | ||
| THRESHOLD="${FAIL_THRESHOLD_INPUT:-70}" | ||
| if [ -z "$DIRS" ]; then | ||
| echo "No directories to analyze" | ||
| exit 0 | ||
| fi | ||
| ALL_PASSED=true | ||
| SUMMARY_FILE="quality_summary.md" | ||
| echo "# 📊 Quality Metrics Dashboard" > $SUMMARY_FILE | ||
| echo "" >> $SUMMARY_FILE | ||
| echo "**Threshold:** $THRESHOLD/100" >> $SUMMARY_FILE | ||
| echo "" >> $SUMMARY_FILE | ||
| for skill_dir in $DIRS; do | ||
| if [ ! -d "$skill_dir" ]; then | ||
| continue | ||
| fi | ||
| SKILL_NAME=$(basename "$skill_dir") | ||
| echo "🔍 Analyzing $SKILL_NAME..." | ||
| # Run quality analysis | ||
| python3 << 'EOF' "$skill_dir" "$THRESHOLD" "$SKILL_NAME" | ||
| import sys | ||
| from pathlib import Path | ||
| sys.path.insert(0, 'src') | ||
| from skill_seekers.cli.quality_metrics import QualityAnalyzer | ||
| skill_dir = Path(sys.argv[1]) | ||
| threshold = float(sys.argv[2]) | ||
| skill_name = sys.argv[3] | ||
| analyzer = QualityAnalyzer(skill_dir) | ||
| report = analyzer.generate_report() | ||
| # Print formatted report | ||
| formatted = analyzer.format_report(report) | ||
| print(formatted) | ||
| # Save individual report | ||
| with open(f'quality_{skill_name}.txt', 'w') as f: | ||
| f.write(formatted) | ||
| # Add to summary | ||
| score = report.overall_score.total_score | ||
| grade = report.overall_score.grade | ||
| status = "✅" if score >= threshold else "❌" | ||
| summary_line = f"{status} **{skill_name}**: {grade} ({score:.1f}/100)" | ||
| print(f"\n{summary_line}") | ||
| with open('quality_summary.md', 'a') as f: | ||
| f.write(f"{summary_line}\n") | ||
| # Set metrics as annotations | ||
| if score < threshold: | ||
| print(f"::error file={skill_dir}/SKILL.md::Quality score {score:.1f} is below threshold {threshold}") | ||
| sys.exit(1) | ||
| elif score < 80: | ||
| print(f"::warning file={skill_dir}/SKILL.md::Quality score {score:.1f} could be improved") | ||
| else: | ||
| print(f"::notice file={skill_dir}/SKILL.md::Quality score {score:.1f} - Excellent!") | ||
| EOF | ||
| if [ $? -ne 0 ]; then | ||
| ALL_PASSED=false | ||
| fi | ||
| echo "" >> $SUMMARY_FILE | ||
| done | ||
| if [ "$ALL_PASSED" = false ]; then | ||
| echo "❌ Some skills failed quality thresholds" | ||
| exit 1 | ||
| else | ||
| echo "✅ All skills passed quality thresholds" | ||
| fi | ||
| - name: Upload quality reports | ||
| uses: actions/upload-artifact@v3 | ||
| with: | ||
| name: quality-metrics-reports | ||
| path: quality_*.txt | ||
| retention-days: 30 | ||
| continue-on-error: true | ||
| - name: Post summary to PR | ||
| if: github.event_name == 'pull_request' | ||
| uses: actions/github-script@v6 | ||
| with: | ||
| script: | | ||
| const fs = require('fs'); | ||
| const summary = fs.readFileSync('quality_summary.md', 'utf8'); | ||
| github.rest.issues.createComment({ | ||
| issue_number: context.issue.number, | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| body: summary | ||
| }); | ||
| continue-on-error: true | ||
| - name: Create dashboard summary | ||
| run: | | ||
| if [ -f "quality_summary.md" ]; then | ||
| cat quality_summary.md >> $GITHUB_STEP_SUMMARY | ||
| fi | ||