name: CI
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Check formatting
run: npm run format:check
typecheck:
name: Type Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run type check
run: npm run typecheck
test:
name: Unit Tests (Node ${{ matrix.node-version }})
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x, 22.x]
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run unit tests
run: npm test
- name: Run tests with coverage
if: matrix.node-version == '20.x'
run: npm test -- --coverage
- name: Upload coverage reports
if: matrix.node-version == '20.x'
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage/lcov.info
flags: unittests
name: codecov-umbrella
fail_ci_if_error: false
- name: Read coverage summary
if: matrix.node-version == '20.x'
id: coverage
run: |
COVERAGE=$(cat coverage/coverage-summary.json | jq '.total.lines.pct')
echo "lines=$COVERAGE" >> $GITHUB_OUTPUT
- name: Create coverage badge
if: matrix.node-version == '20.x' && github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: schneegans/dynamic-badges-action@v1.7.0
with:
auth: ${{ secrets.GIST_TOKEN }}
gistID: YOUR_GIST_ID
filename: enc-charts-mcp-coverage.json
label: Coverage
message: ${{ steps.coverage.outputs.lines }}%
color: ${{ steps.coverage.outputs.lines < 60 && 'red' || steps.coverage.outputs.lines < 80 && 'yellow' || 'brightgreen' }}
- name: Coverage Summary
if: matrix.node-version == '20.x' && always()
run: |
echo "## Coverage Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Type | Coverage | Threshold |" >> $GITHUB_STEP_SUMMARY
echo "|------|----------|-----------|" >> $GITHUB_STEP_SUMMARY
echo "| Lines | $(cat coverage/coverage-summary.json | jq -r '.total.lines.pct')% | 83% |" >> $GITHUB_STEP_SUMMARY
echo "| Branches | $(cat coverage/coverage-summary.json | jq -r '.total.branches.pct')% | 65% |" >> $GITHUB_STEP_SUMMARY
echo "| Functions | $(cat coverage/coverage-summary.json | jq -r '.total.functions.pct')% | 76% |" >> $GITHUB_STEP_SUMMARY
echo "| Statements | $(cat coverage/coverage-summary.json | jq -r '.total.statements.pct')% | 82% |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Directory Coverage" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Directory | Lines | Branches | Functions | Statements |" >> $GITHUB_STEP_SUMMARY
echo "|-----------|-------|----------|-----------|------------|" >> $GITHUB_STEP_SUMMARY
echo "| handlers | $(cat coverage/coverage-summary.json | jq -r '.["src/handlers/"].lines.pct // "N/A"')% | $(cat coverage/coverage-summary.json | jq -r '.["src/handlers/"].branches.pct // "N/A"')% | $(cat coverage/coverage-summary.json | jq -r '.["src/handlers/"].functions.pct // "N/A"')% | $(cat coverage/coverage-summary.json | jq -r '.["src/handlers/"].statements.pct // "N/A"')% |" >> $GITHUB_STEP_SUMMARY
echo "| services | $(cat coverage/coverage-summary.json | jq -r '.["src/services/"].lines.pct // "N/A"')% | $(cat coverage/coverage-summary.json | jq -r '.["src/services/"].branches.pct // "N/A"')% | $(cat coverage/coverage-summary.json | jq -r '.["src/services/"].functions.pct // "N/A"')% | $(cat coverage/coverage-summary.json | jq -r '.["src/services/"].statements.pct // "N/A"')% |" >> $GITHUB_STEP_SUMMARY
- name: Comment PR with coverage
if: matrix.node-version == '20.x' && github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const coverage = require('./coverage/coverage-summary.json');
const comment = `## 📊 Coverage Report
| Type | Coverage | Threshold | Status |
|------|----------|-----------|--------|
| Lines | ${coverage.total.lines.pct}% | 83% | ${coverage.total.lines.pct >= 83 ? '✅' : '❌'} |
| Branches | ${coverage.total.branches.pct}% | 65% | ${coverage.total.branches.pct >= 65 ? '✅' : '❌'} |
| Functions | ${coverage.total.functions.pct}% | 76% | ${coverage.total.functions.pct >= 76 ? '✅' : '❌'} |
| Statements | ${coverage.total.statements.pct}% | 82% | ${coverage.total.statements.pct >= 82 ? '✅' : '❌'} |
${coverage.total.lines.pct >= 83 && coverage.total.branches.pct >= 65 && coverage.total.functions.pct >= 76 && coverage.total.statements.pct >= 82 ?
'✅ All coverage thresholds met!' :
'❌ Coverage thresholds not met. Please add more tests.'}`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: comment
});
build:
name: Build
runs-on: ubuntu-latest
needs: [lint, typecheck]
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build project
run: npm run build
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
retention-days: 7
all-checks-pass:
name: All checks pass
runs-on: ubuntu-latest
needs: [lint, typecheck, test, build]
if: always()
steps:
- name: Verify all checks passed
run: |
if [[ "${{ needs.lint.result }}" != "success" ||
"${{ needs.typecheck.result }}" != "success" ||
"${{ needs.test.result }}" != "success" ||
"${{ needs.build.result }}" != "success" ]]; then
echo "One or more checks failed"
exit 1
fi
echo "All checks passed successfully!"