Skip to content

test(parity): commit the API snapshot so CI actually enforces it #892

test(parity): commit the API snapshot so CI actually enforces it

test(parity): commit the API snapshot so CI actually enforces it #892

Workflow file for this run

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"