fix(knowledge): port api_keys to SQLAlchemy and hard-delete on revoke #7
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 — Postgres compatibility matrix | |
| on: | |
| pull_request: | |
| branches: [develop, main] | |
| paths: | |
| - "dashboard/**" | |
| - "tests/**" | |
| - "pyproject.toml" | |
| - "uv.lock" | |
| - ".github/workflows/ci-postgres.yml" | |
| push: | |
| branches: [develop, main] | |
| paths: | |
| - "dashboard/**" | |
| - "tests/**" | |
| - "pyproject.toml" | |
| - "uv.lock" | |
| - ".github/workflows/ci-postgres.yml" | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| jobs: | |
| # ────────────────────────────────────────────────────────────────────────── | |
| # Guard 1 — forbid raw sqlite3.connect() outside the allowlist | |
| # Guard 2 — forbid raw UPDATE goal_tasks outside the allowlist | |
| # ────────────────────────────────────────────────────────────────────────── | |
| grep-guards: | |
| name: Grep guards (raw sqlite3 + UPDATE goal_tasks) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Guard 1 — Forbid raw sqlite3 in dashboard scope (outside allowlist) | |
| run: | | |
| ALLOWLIST="dashboard/backend/db/|dashboard/alembic/env.py|dashboard/backend/knowledge/|tests/fixtures/" | |
| VIOLATIONS=$(grep -rn "sqlite3\.connect\|^import sqlite3" dashboard/backend/ \ | |
| --include="*.py" \ | |
| | grep -v __pycache__ \ | |
| | grep -vE "$ALLOWLIST" \ | |
| | grep -vE "# noqa.*allowlisted" \ | |
| | grep -v '"""' \ | |
| || true) | |
| if [ -n "$VIOLATIONS" ]; then | |
| echo "::error::Forbidden raw sqlite3 usage outside the allowlist:" | |
| echo "$VIOLATIONS" | |
| exit 1 | |
| fi | |
| echo "Guard 1 passed — no raw sqlite3 outside allowlist." | |
| - name: Guard 2 — Forbid raw UPDATE goal_tasks outside allowlist | |
| run: | | |
| ALLOWLIST="dashboard/alembic/versions/|dashboard/cli/evonexus_migrate\.py|tests/" | |
| VIOLATIONS=$(grep -rEn "UPDATE\s+goal_tasks|text\(['\"].*UPDATE.*goal_tasks" dashboard/ \ | |
| --include="*.py" \ | |
| | grep -v __pycache__ \ | |
| | grep -vE "$ALLOWLIST" \ | |
| || true) | |
| if [ -n "$VIOLATIONS" ]; then | |
| echo "::error::Forbidden raw UPDATE goal_tasks outside the allowlist:" | |
| echo "$VIOLATIONS" | |
| exit 1 | |
| fi | |
| echo "Guard 2 passed — no raw UPDATE goal_tasks outside allowlist." | |
| - name: Guard 3 — yaml.safe_load outside PG-native-configs allowlist (AC10) | |
| run: | | |
| # Files that legitimately call yaml.safe_load (see ALLOWLIST.md §PG-Native-Configs): | |
| # config_store.py — private _get/_set/_list_from_yaml (SQLite-only privates) | |
| # heartbeat_schema.py — load_heartbeats_yaml (SQLite-only) | |
| # plugin_loader.py — import plugin YAML to DB (already PG-guarded) | |
| # plugin_schema.py — plugin manifest metadata reader | |
| # plugin_integration_health.py — plugin manifest health check | |
| # brain_repo/manifest.py — brain repo manifest metadata | |
| # routes/plugins.py — plugin manifest metadata reader | |
| # routes/integrations.py — plugin manifest metadata readers | |
| # routes/_helpers.py — plugin routines discovery (metadata) | |
| # routes/config.py — guarded by get_dialect() == "postgresql" check | |
| # routes/settings.py — _load_yaml called only in SQLite branch | |
| # routes/goals.py — marked noqa: pg-native-configs (SQLite-only path) | |
| # routes/triggers.py — marked noqa: pg-native-configs (SQLite-only path) | |
| # routes/scheduler.py — marked noqa: pg-native-configs (SQLite-only path) | |
| # alembic/versions/ — migration seeds (not runtime) | |
| # claude_hook_dispatcher.py — plugin.yaml metadata only | |
| # routine_store.py — import_from_yaml (CLI migration tool only) | |
| # tests/ — test suite | |
| ALLOWLIST="config_store\.py|heartbeat_schema\.py|plugin_loader\.py|plugin_schema\.py|plugin_integration_health\.py|brain_repo/manifest\.py|routes/plugins\.py|routes/integrations\.py|routes/_helpers\.py|routes/config\.py|routes/settings\.py|alembic/versions/|claude_hook_dispatcher\.py|routine_store\.py|dashboard/cli/|tests/" | |
| VIOLATIONS=$(grep -rn "yaml.safe_load" dashboard/ \ | |
| --include="*.py" \ | |
| | grep -v __pycache__ \ | |
| | grep -vE "$ALLOWLIST" \ | |
| | grep -v "# noqa: pg-native-configs" \ | |
| || true) | |
| if [ -n "$VIOLATIONS" ]; then | |
| echo "::error::yaml.safe_load found outside PG-native-configs allowlist (AC10):" | |
| echo "$VIOLATIONS" | |
| echo "" | |
| echo "Fix: add a dialect guard (if get_dialect() != 'postgresql') OR add" | |
| echo " '# noqa: pg-native-configs' with a comment explaining why it is safe." | |
| exit 1 | |
| fi | |
| echo "Guard 3 passed — no yaml.safe_load outside allowlist." | |
| # ────────────────────────────────────────────────────────────────────────── | |
| # SQLite backend — always runs, must match existing test suite behaviour | |
| # ────────────────────────────────────────────────────────────────────────── | |
| backend-sqlite: | |
| name: Backend tests (SQLite) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 25 | |
| needs: grep-guards | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| - name: Set up uv | |
| uses: astral-sh/setup-uv@v4 | |
| - name: Sync Python dependencies | |
| run: uv sync --frozen --dev | |
| - name: Run backend tests (SQLite) | |
| run: uv run python -m pytest tests/backend/ -m "not postgres" --tb=short -q | |
| # ────────────────────────────────────────────────────────────────────────── | |
| # PostgreSQL backend — uses official postgres:16 service container | |
| # ────────────────────────────────────────────────────────────────────────── | |
| backend-postgres: | |
| name: Backend tests (Postgres 16) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 25 | |
| needs: grep-guards | |
| services: | |
| postgres: | |
| image: postgres:16 | |
| env: | |
| POSTGRES_DB: evonexus_test | |
| POSTGRES_USER: evonexus | |
| POSTGRES_PASSWORD: evonexus_test | |
| ports: | |
| - 5432:5432 | |
| options: >- | |
| --health-cmd="pg_isready -U evonexus" | |
| --health-interval=5s | |
| --health-timeout=5s | |
| --health-retries=10 | |
| env: | |
| DATABASE_URL: postgresql://evonexus:evonexus_test@localhost:5432/evonexus_test | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| - name: Set up uv | |
| uses: astral-sh/setup-uv@v4 | |
| - name: Sync Python dependencies | |
| run: uv sync --frozen --dev | |
| - name: Run Alembic migrations | |
| working-directory: dashboard/alembic | |
| run: uv run alembic upgrade head | |
| - name: Run backend tests (Postgres) | |
| run: uv run python -m pytest tests/backend/ -m "not sqlite" --tb=short -q | |
| - name: Check pool health endpoint | |
| run: | | |
| echo "Pool/dialect checks would run here post-Step-2 when the app is fully PG-capable." |