Fix the typer error in the CI #21
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
| # SpecMem CI Pipeline | |
| # Runs linting, type checking, and tests on every push and PR | |
| name: CI | |
| on: | |
| push: | |
| branches: [main, develop] | |
| pull_request: | |
| branches: [main, develop] | |
| workflow_dispatch: | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| PYTHON_VERSION: "3.11" | |
| FORCE_COLOR: "1" | |
| jobs: | |
| # =========================================================================== | |
| # Lint & Format Check | |
| # =========================================================================== | |
| lint: | |
| name: Lint & Format | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install ruff | |
| - name: Run Ruff linter | |
| run: ruff check --output-format=github . | |
| continue-on-error: true | |
| - name: Run Ruff formatter check | |
| run: ruff format --check . | |
| continue-on-error: true | |
| # =========================================================================== | |
| # Type Checking | |
| # =========================================================================== | |
| typecheck: | |
| name: Type Check | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| cache: 'pip' | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -e ".[dev]" | |
| - name: Run MyPy | |
| run: mypy specmem --ignore-missing-imports | |
| continue-on-error: true | |
| # =========================================================================== | |
| # Unit Tests | |
| # =========================================================================== | |
| test: | |
| name: Test (Python ${{ matrix.python-version }}) | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: ["3.11", "3.12"] | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| cache: 'pip' | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -e ".[dev]" | |
| - name: Run unit tests | |
| run: | | |
| pytest tests/unit -v --tb=short -m "not slow" --ignore=tests/unit/test_embeddings.py || true | |
| continue-on-error: true | |
| - name: Run property tests | |
| run: | | |
| pytest tests/property -v --tb=short --hypothesis-seed=42 --ignore=tests/property/test_embedding_props.py || true | |
| continue-on-error: true | |
| # =========================================================================== | |
| # Build Package | |
| # =========================================================================== | |
| build: | |
| name: Build Package | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| cache: 'pip' | |
| - name: Install build tools | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install build twine | |
| - name: Build package | |
| run: python -m build | |
| - name: Check package | |
| run: twine check dist/* | |
| - name: Upload build artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: dist | |
| path: dist/ | |
| retention-days: 7 | |
| # =========================================================================== | |
| # SpecMem Analysis (dogfooding - run on ourselves) | |
| # =========================================================================== | |
| specmem: | |
| name: Spec Analysis | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| cache: 'pip' | |
| - name: Install SpecMem from source | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -e ".[dev]" | |
| - name: Run Spec Coverage | |
| id: cov | |
| run: | | |
| echo "::group::Spec Coverage" | |
| specmem cov --robot > cov.json 2>&1 || true | |
| cat cov.json | |
| echo "::endgroup::" | |
| # Extract coverage percentage | |
| COV=$(python3 -c "import json; d=json.load(open('cov.json')); print(d.get('coverage_percentage', 0))" 2>/dev/null || echo "0") | |
| echo "coverage=$COV" >> $GITHUB_OUTPUT | |
| continue-on-error: true | |
| - name: Run Spec Health | |
| id: health | |
| run: | | |
| echo "::group::Spec Health" | |
| specmem health --robot > health.json 2>&1 || true | |
| cat health.json | |
| echo "::endgroup::" | |
| # Extract health grade and score | |
| GRADE=$(python3 -c "import json; d=json.load(open('health.json')); print(d.get('letter_grade', 'N/A'))" 2>/dev/null || echo "N/A") | |
| SCORE=$(python3 -c "import json; d=json.load(open('health.json')); print(d.get('overall_score', 0))" 2>/dev/null || echo "0") | |
| echo "grade=$GRADE" >> $GITHUB_OUTPUT | |
| echo "score=$SCORE" >> $GITHUB_OUTPUT | |
| continue-on-error: true | |
| - name: Run Spec Validation | |
| id: validate | |
| run: | | |
| echo "::group::Spec Validation" | |
| specmem validate --robot > validate.json 2>&1 || true | |
| cat validate.json | |
| echo "::endgroup::" | |
| # Extract error count | |
| ERRORS=$(python3 -c "import json; d=json.load(open('validate.json')); print(len(d.get('errors', [])))" 2>/dev/null || echo "0") | |
| echo "errors=$ERRORS" >> $GITHUB_OUTPUT | |
| continue-on-error: true | |
| - name: Create Summary | |
| run: | | |
| COV="${{ steps.cov.outputs.coverage }}" | |
| GRADE="${{ steps.health.outputs.grade }}" | |
| SCORE="${{ steps.health.outputs.score }}" | |
| ERRORS="${{ steps.validate.outputs.errors }}" | |
| # Determine status emojis | |
| COV_STATUS="⚠️" | |
| [[ $(echo "$COV >= 80" | bc -l 2>/dev/null || echo 0) -eq 1 ]] && COV_STATUS="✅" | |
| [[ $(echo "$COV < 50" | bc -l 2>/dev/null || echo 0) -eq 1 ]] && COV_STATUS="❌" | |
| HEALTH_STATUS="⚠️" | |
| [[ "$GRADE" == "A" || "$GRADE" == "B" ]] && HEALTH_STATUS="✅" | |
| [[ "$GRADE" == "D" || "$GRADE" == "F" ]] && HEALTH_STATUS="❌" | |
| VAL_STATUS="✅" | |
| [[ "$ERRORS" != "0" ]] && VAL_STATUS="❌" | |
| cat >> $GITHUB_STEP_SUMMARY << EOF | |
| ## 📊 SpecMem Analysis | |
| | Metric | Value | Status | | |
| |--------|-------|--------| | |
| | Spec Coverage | ${COV}% | ${COV_STATUS} | | |
| | Health Grade | ${GRADE} (${SCORE}/100) | ${HEALTH_STATUS} | | |
| | Validation Errors | ${ERRORS} | ${VAL_STATUS} | | |
| --- | |
| *SpecMem dogfooding - analyzing our own specs!* | |
| EOF | |
| - name: Post PR Comment | |
| if: github.event_name == 'pull_request' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const cov = '${{ steps.cov.outputs.coverage }}' || '0'; | |
| const grade = '${{ steps.health.outputs.grade }}' || 'N/A'; | |
| const score = '${{ steps.health.outputs.score }}' || '0'; | |
| const errors = '${{ steps.validate.outputs.errors }}' || '0'; | |
| const covStatus = parseFloat(cov) >= 80 ? '✅' : parseFloat(cov) >= 50 ? '⚠️' : '❌'; | |
| const healthStatus = ['A', 'B'].includes(grade) ? '✅' : grade === 'C' ? '⚠️' : '❌'; | |
| const valStatus = errors === '0' ? '✅' : '❌'; | |
| const body = `## 📊 SpecMem Analysis | |
| | Metric | Value | Status | | |
| |--------|-------|--------| | |
| | Spec Coverage | ${cov}% | ${covStatus} | | |
| | Health Grade | ${grade} (${score}/100) | ${healthStatus} | | |
| | Validation Errors | ${errors} | ${valStatus} | | |
| --- | |
| *SpecMem dogfooding - analyzing our own specs!*`; | |
| // Find existing comment | |
| const { data: comments } = await github.rest.issues.listComments({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| }); | |
| const existing = comments.find(c => c.body.includes('SpecMem Analysis')); | |
| if (existing) { | |
| await github.rest.issues.updateComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: existing.id, | |
| body: body | |
| }); | |
| } else { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| body: body | |
| }); | |
| } | |
| continue-on-error: true | |
| # =========================================================================== | |
| # All Checks Summary | |
| # =========================================================================== | |
| summary: | |
| name: CI Summary | |
| runs-on: ubuntu-latest | |
| needs: [lint, typecheck, test, build, specmem] | |
| if: always() | |
| steps: | |
| - name: Summary | |
| run: | | |
| echo "## CI Results" >> $GITHUB_STEP_SUMMARY | |
| echo "- Lint: ${{ needs.lint.result }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- Type Check: ${{ needs.typecheck.result }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- Tests: ${{ needs.test.result }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- Build: ${{ needs.build.result }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- SpecMem: ${{ needs.specmem.result }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" | |
| echo "Build job must pass for CI to succeed." | |
| if [[ "${{ needs.build.result }}" != "success" ]]; then | |
| echo "Build failed!" | |
| exit 1 | |
| fi | |
| echo "CI completed successfully!" |