Release/0.8.0.0 #2
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
| name: Code Quality | |
| on: | |
| push: | |
| branches: [ main, new-ast, release/*, develop ] | |
| pull_request: | |
| branches: [ main, new-ast ] | |
| schedule: | |
| # Run weekly to catch quality regressions | |
| - cron: '0 4 * * 2' | |
| jobs: | |
| # Job 1: Formatting and style checks | |
| formatting: | |
| name: Code Formatting | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Setup Haskell | |
| uses: haskell-actions/setup@v2 | |
| with: | |
| ghc-version: '9.8.2' | |
| cabal-version: 'latest' | |
| - name: Install formatting tools | |
| run: | | |
| cabal install ormolu | |
| cabal install stylish-haskell | |
| - name: Check Ormolu formatting | |
| run: | | |
| echo "Checking code formatting with Ormolu..." | |
| ormolu --mode check $(find src test -name "*.hs") | |
| - name: Check import formatting | |
| run: | | |
| echo "Checking import formatting with stylish-haskell..." | |
| stylish-haskell --config .stylish-haskell.yaml --inplace $(find src test -name "*.hs") | |
| # Check if any files were changed | |
| if ! git diff --quiet; then | |
| echo "::error::Import formatting issues found" | |
| git diff | |
| exit 1 | |
| fi | |
| - name: Auto-fix formatting (if enabled) | |
| if: github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'auto-format') | |
| run: | | |
| echo "Auto-fixing formatting issues..." | |
| ormolu --mode inplace $(find src test -name "*.hs") | |
| if ! git diff --quiet; then | |
| git config --local user.email "[email protected]" | |
| git config --local user.name "GitHub Action" | |
| git add . | |
| git commit -m "style: auto-fix formatting with ormolu" | |
| git push | |
| fi | |
| # Job 2: Advanced linting and analysis | |
| linting: | |
| name: Code Linting | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Haskell | |
| uses: haskell-actions/setup@v2 | |
| with: | |
| ghc-version: '9.8.2' | |
| cabal-version: 'latest' | |
| - name: Install linting tools | |
| run: | | |
| cabal install hlint | |
| cabal install apply-refact | |
| - name: Generate lexer and parser | |
| run: | | |
| cabal build --dependencies-only | |
| cabal exec alex -- src/Language/JavaScript/Parser/Lexer.x || echo "Alex generation completed" | |
| cabal exec happy -- src/Language/JavaScript/Parser/Grammar7.y || echo "Happy generation completed" | |
| - name: Run HLint with CLAUDE.md rules | |
| run: | | |
| echo "Running HLint with project-specific rules..." | |
| hlint src/ test/ \ | |
| --report=hlint-report.html \ | |
| --json > hlint-results.json \ | |
| --ignore="Parse error" \ | |
| --ignore="Use camelCase" \ | |
| --ignore="Reduce duplication" | |
| - name: Check CLAUDE.md compliance | |
| run: | | |
| echo "Checking CLAUDE.md compliance..." | |
| # Check for function size limits (≤15 lines) | |
| python3 -c " | |
| import re | |
| import sys | |
| violations = [] | |
| for file in ['src/Language/JavaScript/Parser/AST.hs', 'src/Language/JavaScript/Parser/Parser.hs']: | |
| try: | |
| with open(file, 'r') as f: | |
| content = f.read() | |
| functions = re.findall(r'^(\w+)\s*::', content, re.MULTILINE) | |
| # This is a simplified check - actual implementation would be more complex | |
| if len(functions) > 100: # Heuristic for complexity | |
| violations.append(f'File {file} may have complex functions') | |
| if violations: | |
| for v in violations: | |
| print(f'::warning::{v}') | |
| else: | |
| print('Function size compliance check passed') | |
| except FileNotFoundError: | |
| print(f'File {file} not found') | |
| " | |
| # Check for qualified imports | |
| echo "Checking import patterns..." | |
| if grep -r "^import.*(" src/ | grep -v "qualified"; then | |
| echo "::warning::Found unqualified imports (may violate CLAUDE.md standards)" | |
| fi | |
| # Check for lens usage patterns | |
| echo "Checking lens usage..." | |
| if grep -r "\." src/ | grep -E "\^\.|\&|\.~|%~"; then | |
| echo "::notice::Found lens usage patterns" | |
| fi | |
| - name: Apply automatic refactorings | |
| if: contains(github.event.pull_request.labels.*.name, 'auto-refactor') | |
| run: | | |
| echo "Applying automatic refactorings..." | |
| # Apply HLint suggestions automatically | |
| hlint src/ test/ --refactor --refactor-options="-i" || echo "Refactoring completed" | |
| - name: Upload lint results | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: lint-results | |
| path: | | |
| hlint-report.html | |
| hlint-results.json | |
| # Job 3: Code complexity analysis | |
| complexity: | |
| name: Code Complexity | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Haskell | |
| uses: haskell-actions/setup@v2 | |
| with: | |
| ghc-version: '9.8.2' | |
| cabal-version: 'latest' | |
| - name: Analyze code complexity | |
| run: | | |
| echo "Analyzing code complexity..." | |
| # Line count analysis | |
| echo "## Code Statistics" > complexity-report.md | |
| echo "| Metric | Count |" >> complexity-report.md | |
| echo "|--------|-------|" >> complexity-report.md | |
| echo "| Total Lines | $(find src -name '*.hs' -exec wc -l {} + | tail -1 | awk '{print $1}') |" >> complexity-report.md | |
| echo "| Source Files | $(find src -name '*.hs' | wc -l) |" >> complexity-report.md | |
| echo "| Test Files | $(find test -name '*.hs' | wc -l) |" >> complexity-report.md | |
| # Function count | |
| FUNCTIONS=$(grep -r "^[a-zA-Z][a-zA-Z0-9_]*\s*::" src/ | wc -l) | |
| echo "| Functions | $FUNCTIONS |" >> complexity-report.md | |
| # Module count | |
| MODULES=$(grep -r "^module " src/ | wc -l) | |
| echo "| Modules | $MODULES |" >> complexity-report.md | |
| - name: Check cyclomatic complexity | |
| run: | | |
| echo "Checking cyclomatic complexity..." | |
| # Simplified complexity check by counting if/case/guards | |
| python3 -c " | |
| import os | |
| import re | |
| def analyze_file(filepath): | |
| with open(filepath, 'r') as f: | |
| content = f.read() | |
| # Count complexity indicators | |
| if_count = len(re.findall(r'\bif\b', content)) | |
| case_count = len(re.findall(r'\bcase\b', content)) | |
| guard_count = len(re.findall(r'\|.*=', content)) | |
| where_count = len(re.findall(r'\bwhere\b', content)) | |
| total_complexity = if_count + case_count + guard_count | |
| return { | |
| 'file': filepath, | |
| 'complexity': total_complexity, | |
| 'if': if_count, | |
| 'case': case_count, | |
| 'guards': guard_count, | |
| 'where': where_count | |
| } | |
| high_complexity = [] | |
| for root, dirs, files in os.walk('src'): | |
| for file in files: | |
| if file.endswith('.hs'): | |
| filepath = os.path.join(root, file) | |
| analysis = analyze_file(filepath) | |
| if analysis['complexity'] > 50: # Threshold | |
| high_complexity.append(analysis) | |
| if high_complexity: | |
| print('::warning::High complexity files found:') | |
| for item in high_complexity: | |
| print(f' {item[\"file\"]}: {item[\"complexity\"]} complexity points') | |
| else: | |
| print('Complexity check passed') | |
| " | |
| - name: Upload complexity report | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: complexity-report | |
| path: complexity-report.md | |
| # Job 4: Documentation quality check | |
| documentation: | |
| name: Documentation Quality | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Haskell | |
| uses: haskell-actions/setup@v2 | |
| with: | |
| ghc-version: '9.8.2' | |
| cabal-version: 'latest' | |
| - name: Generate lexer and parser | |
| run: | | |
| cabal build --dependencies-only | |
| cabal exec alex -- src/Language/JavaScript/Parser/Lexer.x | |
| cabal exec happy -- src/Language/JavaScript/Parser/Grammar7.y | |
| - name: Check Haddock documentation | |
| run: | | |
| echo "Checking Haddock documentation coverage..." | |
| cabal haddock --haddock-all 2>&1 | tee haddock.log | |
| # Check for missing documentation | |
| MISSING_DOCS=$(grep -c "Missing documentation" haddock.log || echo 0) | |
| TOTAL_EXPORTS=$(grep -c "Haddock coverage" haddock.log || echo 1) | |
| echo "Documentation coverage report:" > doc-coverage.md | |
| echo "- Missing documentation: $MISSING_DOCS items" >> doc-coverage.md | |
| if [ "$MISSING_DOCS" -gt 10 ]; then | |
| echo "::warning::High number of undocumented items: $MISSING_DOCS" | |
| fi | |
| - name: Check README and changelog | |
| run: | | |
| echo "Checking documentation files..." | |
| # Check if README exists and has content | |
| if [ -s README.md ]; then | |
| echo "✓ README.md exists and has content" | |
| else | |
| echo "::error::README.md is missing or empty" | |
| fi | |
| # Check if changelog exists and is up to date | |
| if [ -s ChangeLog.md ]; then | |
| echo "✓ ChangeLog.md exists" | |
| # Check if latest version is documented | |
| VERSION=$(grep "^Version:" language-javascript.cabal | head -1 | awk '{print $2}') | |
| if grep -q "## $VERSION" ChangeLog.md; then | |
| echo "✓ Current version documented in changelog" | |
| else | |
| echo "::warning::Current version $VERSION not found in changelog" | |
| fi | |
| else | |
| echo "::error::ChangeLog.md is missing" | |
| fi | |
| - name: Upload documentation reports | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: documentation-reports | |
| path: | | |
| haddock.log | |
| doc-coverage.md | |
| # Job 5: Code quality summary | |
| quality-summary: | |
| name: Quality Summary | |
| runs-on: ubuntu-latest | |
| needs: [formatting, linting, complexity, documentation] | |
| if: always() | |
| steps: | |
| - name: Generate quality report | |
| run: | | |
| echo "# Code Quality Summary" > quality-summary.md | |
| echo "" >> quality-summary.md | |
| echo "| Check | Status |" >> quality-summary.md | |
| echo "|-------|--------|" >> quality-summary.md | |
| echo "| Formatting | ${{ needs.formatting.result }} |" >> quality-summary.md | |
| echo "| Linting | ${{ needs.linting.result }} |" >> quality-summary.md | |
| echo "| Complexity | ${{ needs.complexity.result }} |" >> quality-summary.md | |
| echo "| Documentation | ${{ needs.documentation.result }} |" >> quality-summary.md | |
| echo "" >> quality-summary.md | |
| echo "Generated at: $(date -u)" >> quality-summary.md | |
| # Overall quality score | |
| SUCCESS_COUNT=0 | |
| if [ "${{ needs.formatting.result }}" = "success" ]; then SUCCESS_COUNT=$((SUCCESS_COUNT + 1)); fi | |
| if [ "${{ needs.linting.result }}" = "success" ]; then SUCCESS_COUNT=$((SUCCESS_COUNT + 1)); fi | |
| if [ "${{ needs.complexity.result }}" = "success" ]; then SUCCESS_COUNT=$((SUCCESS_COUNT + 1)); fi | |
| if [ "${{ needs.documentation.result }}" = "success" ]; then SUCCESS_COUNT=$((SUCCESS_COUNT + 1)); fi | |
| QUALITY_SCORE=$((SUCCESS_COUNT * 25)) | |
| echo "**Overall Quality Score: $QUALITY_SCORE%**" >> quality-summary.md | |
| - name: Upload quality summary | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: quality-summary | |
| path: quality-summary.md | |
| - name: Set quality status | |
| run: | | |
| if [ "${{ needs.formatting.result }}" = "success" ] && \ | |
| [ "${{ needs.linting.result }}" = "success" ] && \ | |
| [ "${{ needs.complexity.result }}" = "success" ] && \ | |
| [ "${{ needs.documentation.result }}" = "success" ]; then | |
| echo "::notice::All quality checks passed!" | |
| exit 0 | |
| else | |
| echo "::warning::Some quality checks failed" | |
| exit 1 | |
| fi |