name: CI on: push: branches: [main] tags: ["v*"] pull_request: branches: [main] env: CARGO_TERM_COLOR: always RUSTFLAGS: "-D warnings" jobs: check: name: Build & Lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable with: components: rustfmt, clippy - name: Install Python (for PyO3) uses: actions/setup-python@v5 with: python-version: "3.12" - name: Cache cargo registry uses: actions/cache@v4 with: path: | ~/.cargo/registry ~/.cargo/git target key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} restore-keys: | ${{ runner.os }}-cargo- - name: Format check run: cargo fmt --check - name: Clippy run: cargo clippy --workspace -- -D warnings - name: Build run: cargo build --workspace - name: Test run: cargo test --workspace miri: name: Miri (justapi-core) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install Rust nightly + Miri uses: dtolnay/rust-toolchain@nightly with: components: miri - name: Cache cargo registry uses: actions/cache@v4 with: path: | ~/.cargo/registry ~/.cargo/git target key: ${{ runner.os }}-miri-${{ hashFiles('**/Cargo.lock') }} restore-keys: | ${{ runner.os }}-miri- - name: Run Miri on justapi-core run: cargo +nightly miri test -p justapi-core env: MIRIFLAGS: "-Zmiri-disable-isolation" pytest: name: Pytest (justapi-py) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install Python uses: actions/setup-python@v5 with: python-version: "3.12" - name: Install dependencies run: | python -m pip install --upgrade pip pip install pytest httpx pydantic typing_extensions uv maturin - name: Build and install justapi run: | maturin develop -m crates/justapi-py/Cargo.toml - name: Run pytest run: | PYTHONPATH=./crates/justapi-py/python pytest pytest-freethreaded: name: Pytest (free-threaded / no GIL) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install uv uses: astral-sh/setup-uv@v5 - name: Install free-threaded CPython run: uv python install cpython-3.14+freethreaded - name: Create venv and install deps run: | uv venv --python cpython-3.14+freethreaded .venv-ft uv pip install --python .venv-ft pytest pytest_asyncio pydantic jinja2 httpx maturin - name: Build and install justapi (free-threaded) run: | . .venv-ft/bin/activate maturin develop -m crates/justapi-py/Cargo.toml - name: Run pytest with the GIL disabled run: | . .venv-ft/bin/activate PYTHON_GIL=0 PYTHONPATH=./crates/justapi-py/python pytest verify-rust-first: name: Verify Rust-first mandate (AGENTS.md §2) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Ensure scheduling/concurrency is not re-implemented in Python run: | echo "Rust-first mandate (AGENTS.md §2): scheduling, worker/thread pools," echo "and concurrency primitives must live in Rust, not in the Python package." matches=$(grep -rnE "^\s*class\s+\w*(Pool|Scheduler|Worker|Executor|Thread)\w*" \ crates/justapi-py/python/justapi/ --include="*.py" \ | grep -v "/test_") if [ -n "$matches" ]; then echo "::error::Framework Python re-implements a Rust-owned subsystem (scheduling/thread pool):" echo "$matches" echo "Move this into justapi-core / justapi-py Rust. See AGENTS.md §2." exit 1 fi echo "OK: no Python-side scheduling/thread-pool classes found." audit: name: Security Audit (cargo-audit) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install cargo-audit run: cargo install cargo-audit --locked - name: Cache cargo registry uses: actions/cache@v4 with: path: | ~/.cargo/registry ~/.cargo/git key: ${{ runner.os }}-audit-${{ hashFiles('**/Cargo.lock') }} - name: Run audit run: cargo audit deny: name: License & Dependency Check (cargo-deny) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install cargo-deny uses: EmbarkStudios/cargo-deny-action@v2 with: command: check arguments: --all-features docker: name: Build & Push Docker Image runs-on: ubuntu-latest needs: [check] if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) steps: - uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to GitHub Container Registry uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract metadata (tags, labels) for Docker id: meta uses: docker/metadata-action@v5 with: images: ghcr.io/${{ github.repository }} tags: | type=ref,event=branch type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=sha,format=long - name: Build and push Docker image uses: docker/build-push-action@v5 with: context: . push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max