name: CI # CI lanes per FabledRulebook forgejo.md "CI philosophy": # - lint: ruff only, no dep install — fast-fail for the common lint bounce. # - unit: pytest -m "not integration"; no service containers, no DB. # - integration: postgres service container; boots the real app + runs all # (core + bundled-plugin) migrations. # Single repo: first-party plugins are bundled in-tree under plugins/, so the # unit lane imports them directly — no cross-repo checkout. on: push: branches: [dev, main] # pull_request intentionally absent — push on [dev, main] already fires CI for # every dev commit and dev→main PRs. Single-operator repo, no fork PRs. jobs: # Fast-fail lint lane. ruff is pre-installed in the ci-python image, so this # runs with NO dependency install and surfaces lint bounces in seconds. lint: runs-on: python-ci container: image: git.fabledsword.com/bvandeusen/ci-python:3.14 steps: - uses: actions/checkout@v4 - name: Ruff lint run: ruff check steward/ plugins/ tests/ unit: runs-on: python-ci container: image: git.fabledsword.com/bvandeusen/ci-python:3.14 steps: - uses: actions/checkout@v4 - name: Install Steward + test deps # uv: 5-10x faster wheel resolve than pip on cold caches. Falls back to # pip on uv-missing runners. Steward declares its deps in pyproject; the # [dev] extra adds pytest + pytest-asyncio. run: | if command -v uv >/dev/null 2>&1; then uv pip install --system -e '.[dev]' else pip install -e '.[dev]' fi - name: Pytest (unit only — integration runs in its own lane) run: pytest tests/ -v -m "not integration" integration: runs-on: python-ci container: image: git.fabledsword.com/bvandeusen/ci-python:3.14 env: STEWARD_SECRET_KEY: ci_integration_placeholder services: postgres: image: postgres:16-alpine env: POSTGRES_USER: steward POSTGRES_PASSWORD: steward POSTGRES_DB: steward options: >- --health-cmd "pg_isready -U steward" --health-interval 10s --health-timeout 5s --health-retries 10 steps: - uses: actions/checkout@v4 - name: Boot + migrate against Postgres # act_runner (swarm-runner) puts service containers on the default bridge # with no embedded DNS, so the hostname `postgres` won't resolve — we # discover the service container's bridge IP via the docker socket. The # job name `integration` (no underscore) is the docker-ps name filter. run: | set -euxo pipefail echo "=== container landscape (diagnostic for filter scoping) ===" docker ps -a --format '{{.ID}} {{.Image}} -> {{.Names}}' echo "=== end landscape ===" PG=$(docker ps --filter "name=integration" --filter "ancestor=postgres:16-alpine" -q | head -n1) test -n "$PG" PG_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$PG") test -n "$PG_IP" export STEWARD_DATABASE_URL="postgresql+asyncpg://steward:steward@$PG_IP:5432/steward" for i in $(seq 1 60); do (echo > "/dev/tcp/$PG_IP/5432") >/dev/null 2>&1 && break sleep 2 done if command -v uv >/dev/null 2>&1; then uv pip install --system -e '.[dev]' else pip install -e '.[dev]' fi pytest tests/integration -v -m integration --durations=10