Skip to content

Release/0.8.0.0

Release/0.8.0.0 #4

Workflow file for this run

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 code compliance
run: |
echo "Checking code 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