Abdr007's picture
Lexora — deployed tree
3fc8e60
Raw
History Blame Contribute Delete
4.46 kB
name: CI
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
# Keeps the ONNX cache in one place so actions/cache can hold it across runs.
LEXORA_VAR_DIR: ${{ github.workspace }}/var
jobs:
api:
name: API lint, types, tests
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- uses: actions/checkout@v7
# setup-uv publishes no floating major tag, so this is pinned exactly;
# the others track a major that GitHub keeps pointed at a Node 24 release.
- uses: astral-sh/setup-uv@v9.0.0
with:
enable-cache: true
- name: Install
run: |
uv venv --python 3.12 apps/api/.venv
uv pip install --python apps/api/.venv/bin/python -e "apps/api[dev]"
# The models are ~200 MB. Caching them turns a 3-minute download into seconds
# and keeps CI from hammering the Hugging Face CDN on every push.
- name: Cache ONNX models
uses: actions/cache@v6
with:
path: var/models
key: onnx-${{ runner.os }}-${{ hashFiles('apps/api/pyproject.toml') }}
- name: Fetch the corpus
run: apps/api/.venv/bin/python corpus/download.py
- name: Build the index
run: apps/api/.venv/bin/python -m app.rag.index
env:
PYTHONPATH: apps/api
- name: Verify the eval labels against the corpus
run: apps/api/.venv/bin/python eval/build_questions.py
- name: ruff
run: |
apps/api/.venv/bin/ruff check .
apps/api/.venv/bin/ruff format --check .
- name: mypy --strict
run: apps/api/.venv/bin/mypy
- name: pytest
run: apps/api/.venv/bin/python -m pytest
# A regression in retrieval quality fails the build like any other test.
# The gate lives in scripts/quality_gate.py so CI and `make ci` run identical
# code — a duplicated inline script is a gate that drifts.
- name: Evaluate retrieval and gate on it
run: |
apps/api/.venv/bin/python eval/ragas_run.py
apps/api/.venv/bin/python scripts/quality_gate.py
- uses: actions/upload-artifact@v7
if: always()
with:
name: eval-results
path: eval/results/
web:
name: Web types, lint, build
runs-on: ubuntu-latest
timeout-minutes: 15
defaults:
run:
working-directory: apps/web
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: "22"
cache: npm
cache-dependency-path: apps/web/package-lock.json
- run: npm ci
- run: npx tsc --noEmit
- run: npx eslint . --max-warnings 0
- run: npm run build
container:
name: Container builds
runs-on: ubuntu-latest
timeout-minutes: 30
needs: api
steps:
- uses: actions/checkout@v7
- uses: astral-sh/setup-uv@v9.0.0
- name: Rebuild the portable index the image copies in
run: |
uv venv --python 3.12 apps/api/.venv
uv pip install --python apps/api/.venv/bin/python -e "apps/api"
apps/api/.venv/bin/python corpus/download.py
PYTHONPATH=apps/api apps/api/.venv/bin/python -m app.rag.index
# `shell: bash` is load-bearing: the default is `bash -e` without pipefail, so a
# failing `docker build` piped into `tee` would exit 0 and pass this step.
- name: Build the image
shell: bash
run: docker build --progress=plain -t lexora-api:ci . 2>&1 | tee build.log
# A runner has gigabytes to spare, so a successful `docker build` says nothing
# about a constrained builder. Two deploys were lost to a build-time OOM that CI
# could not see. Gate on the peak RSS each bake stage reports instead.
- name: Bake stages stayed within their memory budget
run: apps/api/.venv/bin/python scripts/check_bake_budget.py build.log
- name: Boot it and check readiness
run: |
docker run -d --name lexora -p 7860:7860 lexora-api:ci
for _ in $(seq 1 60); do
if curl -fsS http://127.0.0.1:7860/api/health | grep -q '"status":"ok"'; then
echo "container healthy"; exit 0
fi
sleep 5
done
echo "container did not become healthy"; docker logs lexora; exit 1