2121 - uses : actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
2222 with :
2323 python-version : ${{ matrix.python-version }}
24- - name : Install (core + server/mcp/code extras; no torch — the offline gate )
24+ - name : Install (core + server/mcp/code extras; no torch or SQLCipher )
2525 run : |
2626 python -m pip install --upgrade pip
2727 pip install -e ".[test]"
4242 - name : Ablation (vector-only vs hybrid)
4343 run : python -m eval.ablation
4444
45+ encryption :
46+ name : encryption driver gate (Python ${{ matrix.python-version }})
47+ runs-on : ubuntu-latest
48+ strategy :
49+ fail-fast : false
50+ matrix :
51+ python-version : ["3.10", "3.11", "3.12", "3.13", "3.14"]
52+ steps :
53+ - uses : actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
54+ - uses : actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
55+ with :
56+ python-version : ${{ matrix.python-version }}
57+ - name : Install encryption integration gate
58+ run : |
59+ python -m pip install --upgrade pip
60+ pip install -e ".[test,encryption]"
61+ # sqlcipher3-binary currently cannot coexist safely with the stdlib sqlite
62+ # extension during the long general suite. Keep its real driver contract in
63+ # this dedicated, short-lived process rather than skipping encryption coverage.
64+ - name : Encryption at-rest integration tests
65+ run : python -m pytest -o addopts="" tests/test_encrypted_store.py -q -rs
66+
4567 core-py39 :
4668 name : core floor (numpy-only, Python 3.9)
4769 runs-on : ubuntu-latest
6183 - name : Ablation
6284 run : python -m eval.ablation
6385
86+ pi-extension :
87+ name : Pi extension (${{ matrix.os }}, Python ${{ matrix.python-version }}, Node ${{ matrix.node-version }})
88+ runs-on : ${{ matrix.os }}
89+ strategy :
90+ fail-fast : false
91+ matrix :
92+ include :
93+ - os : ubuntu-latest
94+ python-version : " 3.10"
95+ node-version : " 22.19.0"
96+ - os : windows-latest
97+ python-version : " 3.11"
98+ node-version : " 24"
99+ steps :
100+ - uses : actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
101+ - uses : actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
102+ with :
103+ python-version : ${{ matrix.python-version }}
104+ - uses : actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
105+ with :
106+ node-version : ${{ matrix.node-version }}
107+ cache : npm
108+ cache-dependency-path : integrations/pi/npm-shrinkwrap.json
109+ - name : Install the current Smart MCP server
110+ run : |
111+ python -m pip install --upgrade pip
112+ python -m pip install -e ".[test]"
113+ - name : Install and verify the Pi package
114+ working-directory : integrations/pi
115+ env :
116+ ENGRAPHIS_PI_TEST_COMMAND : engraphis-mcp
117+ run : |
118+ npm ci --ignore-scripts
119+ npm run verify
120+ npm run test:integration
121+ npm audit --omit=dev
122+
64123 browser-accessibility :
65124 name : browser accessibility smoke
66125 runs-on : ubuntu-latest
98157 if [ "${{ github.event_name }}" != "pull_request" ]; then
99158 echo "run=true" >> "$GITHUB_OUTPUT"
100159 elif git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.sha }}" \
101- | grep -qE '^(Dockerfile|docker-entrypoint\.sh|docker-compose\.yml|railway\.json|deploy/|\.dockerignore|engraphis/|scripts/|pyproject\.toml|\.github/workflows/ci\.yml)'; then
160+ | grep -qE '^(Dockerfile|docker-entrypoint\.sh|docker-compose(\.lan)? \.yml|railway\.json|deploy/|\.dockerignore|engraphis/|scripts/|pyproject\.toml|\.github/workflows/ci\.yml)'; then
102161 echo "run=true" >> "$GITHUB_OUTPUT"
103162 else
104163 echo "run=false" >> "$GITHUB_OUTPUT"
@@ -111,6 +170,18 @@ jobs:
111170 if : needs.docker-gate.outputs.run == 'true'
112171 steps :
113172 - uses : actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
173+ - name : Validate Compose configuration
174+ run : docker compose config --quiet
175+ - name : Reject unauthenticated LAN Compose overlay
176+ run : |
177+ if env -u ENGRAPHIS_API_TOKEN docker compose -f docker-compose.yml -f docker-compose.lan.yml config --quiet; then
178+ echo "LAN overlay must require ENGRAPHIS_API_TOKEN"
179+ exit 1
180+ fi
181+ - name : Validate token-protected LAN Compose overlay
182+ env :
183+ ENGRAPHIS_API_TOKEN : ci-lan-overlay-token
184+ run : docker compose -f docker-compose.yml -f docker-compose.lan.yml config --quiet
114185 - name : Build image
115186 run : docker build -t engraphis:ci .
116187 - name : Verify production image OCR runtime
@@ -119,10 +190,22 @@ jobs:
119190 'python -c "import PIL, pytesseract" && command -v tesseract >/dev/null &&
120191 tesseract --version | head -n 1'
121192 - name : Audit the exact production image dependency set
122- run : >-
123- docker run --rm --entrypoint sh engraphis:ci -c
124- 'python -m pip install --no-cache-dir pip-audit &&
125- python -m pip_audit --local'
193+ # The runtime image intentionally has no pip: it is a build tool whose vendored
194+ # dependency snapshot would otherwise remain an unnecessary attack surface. Copy the
195+ # exact installed distributions to the runner and audit that set instead of mutating
196+ # the production image just to run the audit.
197+ run : |
198+ audit_dir="$(mktemp -d)"
199+ container="engraphis-audit-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
200+ cleanup() {
201+ docker rm -f "$container" >/dev/null 2>&1 || true
202+ rm -rf "$audit_dir"
203+ }
204+ trap cleanup EXIT
205+ python -m pip install --disable-pip-version-check --no-cache-dir pip-audit
206+ docker create --name "$container" engraphis:ci >/dev/null
207+ docker cp "$container":/usr/local/lib/python3.11/site-packages/. "$audit_dir"
208+ python -m pip_audit --path "$audit_dir"
126209 - name : Run container (offline deterministic embedder — no model downloads)
127210 run : |
128211 docker run -d --name engraphis -p 8700:8700 \
0 commit comments