fix: close remaining consolidation review findings #636
Workflow file for this run
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: ci | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| permissions: | |
| contents: read | |
| jobs: | |
| test: | |
| name: test + lint (full offline stack) | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| cache: pip | |
| cache-dependency-path: pyproject.toml | |
| - name: Install (core + server/mcp/code extras; no torch or SQLCipher) | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -e ".[test]" | |
| - name: Lint (ruff) | |
| run: ruff check . | |
| - name: Commercial manifest + strict-CSP drift gate | |
| run: | | |
| python scripts/check_commercial_manifest.py | |
| python scripts/externalize_dashboard_assets.py | |
| - name: Unit tests (full suite — extras-gated tests included) | |
| run: | | |
| python -c "import fastapi, httpx, mcp, multipart, pydantic, uvicorn" | |
| python -m pytest -o addopts="" tests/ -q -rs | |
| - name: Retrieval eval gate | |
| run: python -m eval.harness --dataset eval/datasets/sample.jsonl --k 5 | |
| - name: Retrieval eval gate — CodeMem (coding-agent wedge, incl. conflict resolution) | |
| run: python -m eval.harness --dataset eval/datasets/codemem.jsonl --k 5 | |
| - name: Ablation (vector-only vs hybrid) | |
| run: python -m eval.ablation | |
| encryption: | |
| name: encryption driver gate (Python ${{ matrix.python-version }}) | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| cache: pip | |
| cache-dependency-path: pyproject.toml | |
| - name: Install encryption integration gate | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -e ".[test,encryption]" | |
| # sqlcipher3-binary currently cannot coexist safely with the stdlib sqlite | |
| # extension during the long general suite. Keep its real driver contract in | |
| # this dedicated, short-lived process rather than skipping encryption coverage. | |
| - name: Encryption at-rest integration tests | |
| run: python -m pytest -o addopts="" tests/test_encrypted_store.py -q -rs | |
| core-py39: | |
| name: core floor (numpy-only, Python 3.9) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: "3.9" | |
| cache: pip | |
| cache-dependency-path: pyproject.toml | |
| - name: Install (numpy-only core — the minimum supported runtime) | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install numpy pytest | |
| - name: Unit tests (extras-gated tests skip; the core must pass) | |
| run: python -m pytest -o addopts="" tests/ -q -rs | |
| - name: Retrieval eval gate | |
| run: python -m eval.harness --dataset eval/datasets/sample.jsonl --k 5 | |
| - name: Ablation | |
| run: python -m eval.ablation | |
| coverage: | |
| name: coverage gate (Python 3.11) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: "3.11" | |
| cache: pip | |
| cache-dependency-path: pyproject.toml | |
| - name: Install | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -e ".[test]" pytest-cov | |
| - name: Coverage run (all extras-gated tests, tracked modules) | |
| run: python -m pytest -o addopts="" tests/ -q -rs --cov=engraphis --cov-report=term-missing --cov-fail-under=60 | |
| hygiene: | |
| name: repo hygiene gate (no stray DBs/logs) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - name: Reject stray runtime artifacts at repo root | |
| run: | | |
| stray=$(find . -maxdepth 1 -type f \( -name '*.db' -o -name '*.db-wal' -o -name '*.db-shm' \ | |
| -o -name '*.bak' -o -name '*.log' \) -print) | |
| if [ -n "$stray" ]; then | |
| echo "Refusing to commit stray runtime artifacts:"; echo "$stray"; exit 1 | |
| fi | |
| echo "repo root clean" | |
| pi-extension: | |
| name: Pi extension (${{ matrix.os }}, Python ${{ matrix.python-version }}, Node ${{ matrix.node-version }}) | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| python-version: "3.10" | |
| node-version: "22.19.0" | |
| - os: windows-latest | |
| python-version: "3.11" | |
| node-version: "24" | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| node-version: ${{ matrix.node-version }} | |
| cache: npm | |
| cache-dependency-path: integrations/pi/npm-shrinkwrap.json | |
| - name: Install the current Smart MCP server | |
| run: | | |
| python -m pip install --upgrade pip | |
| python -m pip install -e ".[test]" | |
| - name: Install and verify the Pi package | |
| working-directory: integrations/pi | |
| env: | |
| ENGRAPHIS_PI_TEST_COMMAND: engraphis-mcp | |
| run: | | |
| npm ci --ignore-scripts | |
| npm run verify | |
| npm run test:integration | |
| npm audit --omit=dev | |
| browser-accessibility: | |
| name: browser accessibility smoke | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: "3.11" | |
| - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| node-version: "24" | |
| - name: Install dashboard and browser test dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -e ".[test]" "uvicorn[standard]>=0.29" | |
| npm ci | |
| npx playwright install --with-deps chromium | |
| - name: Playwright desktop/mobile, keyboard, CSP, console, and axe checks | |
| run: npx playwright test | |
| docker-gate: | |
| # Keeps CI fast: the docker job always runs on push to main, but on PRs only | |
| # when image/deployment paths changed (Dockerfile, entrypoint, Railway, source, | |
| # pyproject.toml, or this workflow). No third-party actions — plain git diff. | |
| name: docker smoke — path gate | |
| runs-on: ubuntu-latest | |
| outputs: | |
| run: ${{ steps.decide.outputs.run }} | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| with: | |
| fetch-depth: 0 | |
| - id: decide | |
| run: | | |
| if [ "${{ github.event_name }}" != "pull_request" ]; then | |
| echo "run=true" >> "$GITHUB_OUTPUT" | |
| elif git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.sha }}" \ | |
| | grep -qE '^(Dockerfile|docker-entrypoint\.sh|docker-compose(\.lan)?\.yml|railway\.json|deploy/|\.dockerignore|engraphis/|scripts/|pyproject\.toml|\.github/workflows/ci\.yml)'; then | |
| echo "run=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "run=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| docker-smoke: | |
| name: docker build + health smoke | |
| runs-on: ubuntu-latest | |
| needs: docker-gate | |
| if: needs.docker-gate.outputs.run == 'true' | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - name: Validate Compose configuration | |
| run: docker compose config --quiet | |
| - name: Reject unauthenticated LAN Compose overlay | |
| run: | | |
| if env -u ENGRAPHIS_API_TOKEN docker compose -f docker-compose.yml -f docker-compose.lan.yml config --quiet; then | |
| echo "LAN overlay must require ENGRAPHIS_API_TOKEN" | |
| exit 1 | |
| fi | |
| - name: Validate token-protected LAN Compose overlay | |
| env: | |
| ENGRAPHIS_API_TOKEN: ci-lan-overlay-token | |
| run: docker compose -f docker-compose.yml -f docker-compose.lan.yml config --quiet | |
| - name: Build image | |
| run: docker build -t engraphis:ci . | |
| - name: Verify production image OCR runtime | |
| run: >- | |
| docker run --rm --entrypoint sh engraphis:ci -c | |
| 'python -c "import PIL, pytesseract" && command -v tesseract >/dev/null && | |
| tesseract --version | head -n 1' | |
| - name: Audit the exact production image dependency set | |
| # The runtime image intentionally has no pip: it is a build tool whose vendored | |
| # dependency snapshot would otherwise remain an unnecessary attack surface. Copy the | |
| # exact installed distributions to the runner and audit that set instead of mutating | |
| # the production image just to run the audit. | |
| run: | | |
| audit_dir="$(mktemp -d)" | |
| container="engraphis-audit-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" | |
| cleanup() { | |
| docker rm -f "$container" >/dev/null 2>&1 || true | |
| rm -rf "$audit_dir" | |
| } | |
| trap cleanup EXIT | |
| python -m pip install --disable-pip-version-check --no-cache-dir pip-audit | |
| docker create --name "$container" engraphis:ci >/dev/null | |
| docker cp "$container":/usr/local/lib/python3.11/site-packages/. "$audit_dir" | |
| python -m pip_audit --path "$audit_dir" | |
| - name: Run container (offline deterministic embedder — no model downloads) | |
| run: | | |
| docker run -d --name engraphis -p 8700:8700 \ | |
| -e ENGRAPHIS_EMBED_MODEL= \ | |
| -e ENGRAPHIS_LOOP_INTERVAL=0 \ | |
| -e ENGRAPHIS_HOST=0.0.0.0 \ | |
| -e ENGRAPHIS_SERVICE_MODE=customer \ | |
| engraphis:ci | |
| - name: Wait for /api/health, then check /api/ready | |
| run: | | |
| for i in $(seq 1 60); do | |
| code=$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8700/api/health || true) | |
| if [ "$code" = "200" ]; then | |
| echo "healthy after ~${i}s" | |
| curl -fsS http://127.0.0.1:8700/api/ready | |
| exit 0 | |
| fi | |
| sleep 1 | |
| done | |
| echo "server never became healthy" | |
| docker logs engraphis | |
| exit 1 | |
| - name: Teardown | |
| if: always() | |
| run: docker rm -f engraphis | |
| build: | |
| name: build + install wheel | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: "3.11" | |
| - name: Build sdist + wheel and verify a clean install | |
| run: | | |
| python -m pip install --upgrade pip build pip-audit | |
| python -m build | |
| python scripts/verify_distribution_contents.py dist/* | |
| python -m venv .audit-venv | |
| .audit-venv/bin/python -m pip install --upgrade "pip>=26.1.2" "setuptools>=83" | |
| .audit-venv/bin/python -m pip install dist/*.whl | |
| AUDIT_SITE=$(.audit-venv/bin/python -c "import site; print(site.getsitepackages()[0])") | |
| python -m pip_audit --path "$AUDIT_SITE" | |
| .audit-venv/bin/python -c "import engraphis, eval.harness; print('wheel imports OK')" |