Skip to content

Merge pull request #51 from Coding-Dev-Tools/codex/railway-ready-final #313

Merge pull request #51 from Coding-Dev-Tools/codex/railway-ready-final

Merge pull request #51 from Coding-Dev-Tools/codex/railway-ready-final #313

Workflow file for this run

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"]
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
with:
python-version: ${{ matrix.python-version }}
- name: Install (core + server/mcp/code extras; no torch — the offline gate)
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 -m pytest tests/ -q
- 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
core-py39:
name: core floor (numpy-only, Python 3.9)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
with:
python-version: "3.9"
- 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 tests/ -q
- name: Retrieval eval gate
run: python -m eval.harness --dataset eval/datasets/sample.jsonl --k 5
- name: Ablation
run: python -m eval.ablation
browser-accessibility:
name: browser accessibility smoke
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
with:
python-version: "3.11"
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.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@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # 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\.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@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- 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
run: >-
docker run --rm --entrypoint sh engraphis:ci -c
'python -m pip install --no-cache-dir pip-audit &&
python -m pip_audit --local'
- 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@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
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 -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; print('wheel import OK')"