test(parity): commit the API snapshot so CI actually enforces it #892
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: M3 Memory CI | |
| on: | |
| push: | |
| branches: [ "main" ] | |
| pull_request: | |
| branches: [ "main" ] | |
| permissions: | |
| contents: read | |
| jobs: | |
| lint: | |
| name: Lint (Ruff) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| persist-credentials: false | |
| - name: Set up Python | |
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: "3.12" | |
| cache: 'pip' | |
| - name: Install Ruff | |
| run: pip install ruff | |
| - name: Run Ruff | |
| # bench_*.py / benchmark_*.py are excluded: they carry the same mechanical | |
| # lint debt, but editing them trips the pre-push bench-data guard (their | |
| # source references private benchmark dataset paths). Lint them via a | |
| # separate bench-aware change, not the standard CI gate. | |
| run: ruff check bin/ memory/ --exclude 'bench_*.py' --exclude 'benchmark_*.py' | |
| typecheck: | |
| name: Type Check (Mypy) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| persist-credentials: false | |
| - name: Set up Python | |
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: "3.12" | |
| cache: 'pip' | |
| - name: Install dependencies | |
| run: | | |
| pip install -r requirements.txt | |
| pip install types-PyYAML | |
| - name: Run Mypy | |
| run: mypy bin/ --ignore-missing-imports | |
| security: | |
| name: Security scan (Bandit + pip-audit on core deps) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| persist-credentials: false | |
| - name: Set up Python | |
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: "3.12" | |
| cache: 'pip' | |
| # Bandit: static analysis on all code paths. Config (skips, exclusions) | |
| # lives in pyproject.toml under [tool.bandit]. | |
| - name: Install Bandit | |
| run: pip install "bandit[toml]" | |
| - name: Run Bandit | |
| run: bandit -c pyproject.toml -r bin/ m3_memory/ | |
| # pip-audit: scoped to CORE deps only (no [dev] / no opt-in rerank | |
| # path). Bench/dev transitive CVEs (e.g. transformers, lxml) shouldn't | |
| # gate merges since they don't reach end users; but a CVE in a real | |
| # shipped dep should fail the build immediately. We install the | |
| # package itself (core deps only — no extras) into a clean venv, then | |
| # audit only the locally-installed transitive tree (-l). The local | |
| # flag also skips m3-memory itself, which would otherwise fail audit | |
| # before its release lands on PyPI (chicken-and-egg in pre-release CI). | |
| - name: Install M3 (core deps only) into a clean venv | |
| run: | | |
| python -m venv /tmp/m3-core | |
| /tmp/m3-core/bin/pip install --upgrade pip | |
| # Install M3 to pull its core dependencies, then uninstall the | |
| # package itself. pip-audit -l would otherwise try to look up | |
| # m3-memory on PyPI and fail with "not found" before each release | |
| # tag is published (chicken-and-egg in pre-release CI). | |
| /tmp/m3-core/bin/pip install . | |
| /tmp/m3-core/bin/pip uninstall -y m3-memory | |
| /tmp/m3-core/bin/pip install pip-audit | |
| - name: pip-audit (core deps, locally installed) | |
| # CVE-2026-3219: pip itself — present because pip is in every venv, | |
| # but pip is not a shipped runtime dependency of m3-memory. Ignored | |
| # so the audit signal stays focused on actual shipped-library CVEs. | |
| # | |
| # PYSEC-2025-183: pyjwt "weak encryption" — affects ALL pyjwt versions | |
| # (0.1.1–2.12.1) with no fixed version published, so it cannot be | |
| # resolved by upgrading. The advisory is disputed by the pyjwt | |
| # maintainers: the concern is the key length chosen by the calling | |
| # application, not a flaw in the library. m3-memory's own code never | |
| # imports `jwt`; pyjwt is present only transitively via the `mcp` | |
| # core dependency, so the weak-key-length path is not reachable | |
| # through m3-memory. Ignored; revisit if pyjwt ships a real fix. | |
| run: /tmp/m3-core/bin/pip-audit --strict -l --ignore-vuln CVE-2026-3219 --ignore-vuln PYSEC-2025-183 | |
| # Emit the test matrix based on event type. Pull requests get a single fast | |
| # cell (ubuntu + 3.12) for quick feedback; pushes to main/release run the | |
| # full OS × Python grid so cross-platform coverage still gates the branch | |
| # everything merges into. A bug that only shows on macOS/Windows or 3.11 is | |
| # caught at merge time, not on every PR iteration. | |
| matrix-setup: | |
| name: Resolve test matrix | |
| runs-on: ubuntu-latest | |
| outputs: | |
| matrix: ${{ steps.set.outputs.matrix }} | |
| steps: | |
| - id: set | |
| run: | | |
| if [ "${{ github.event_name }}" = "pull_request" ]; then | |
| echo 'matrix={"os":["ubuntu-latest"],"python-version":["3.12"]}' >> "$GITHUB_OUTPUT" | |
| else | |
| echo 'matrix={"os":["ubuntu-latest","macos-latest","windows-latest"],"python-version":["3.11","3.12"]}' >> "$GITHUB_OUTPUT" | |
| fi | |
| test: | |
| name: Test on ${{ matrix.os }} (py${{ matrix.python-version }}) | |
| needs: [lint, typecheck, matrix-setup] | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: ${{ fromJSON(needs.matrix-setup.outputs.matrix) }} | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| persist-credentials: false | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| cache: 'pip' | |
| - name: Install Dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -r requirements.txt | |
| - name: Run Pytest (hermetic lane) | |
| # The hermetic unit lane: everything that needs no external service and is | |
| # not timing-sensitive. | |
| # - `not integration` excludes tests needing a live service (requires_pg / | |
| # requires_embedder / requires_gguf / requires_files_db) — GH runners | |
| # provision none, so they'd only self-skip. Run them on a dev box (or a | |
| # future services job) with `pytest -m integration`. | |
| # - `not slow` excludes the perf tests (test_chatlog_perf). Their wall-clock | |
| # budgets (e.g. enqueue < 200ms) assume a quiet, dedicated host; a shared | |
| # ubuntu-latest runner misses them intermittently (measured 213ms vs 200ms | |
| # budget — a scheduling flake, not a regression). Perf belongs on a | |
| # baseline-stable host, not the PR gate. Run locally with `pytest -m slow`. | |
| # --strict-markers makes a typo'd marker a hard error. | |
| # See docs/design/TEST_SUITE_DESIGN.md. | |
| run: | | |
| pytest tests/ -m "not integration and not slow" |