Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 46 additions & 112 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,113 +22,69 @@ concurrency:
cancel-in-progress: true

jobs:
# Fast version consistency check
version-check:
name: Version Sync
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v4

- name: Check Python ↔ Rust version consistency
run: |
PYTHON_VERSION=$(grep -E "^version = " pyproject.toml | head -1 | cut -d'"' -f2)
RUST_VERSION=$(grep -E "^version = " rust/Cargo.toml | head -1 | cut -d'"' -f2)

echo "Python: $PYTHON_VERSION"
echo "Rust: $RUST_VERSION"

if [ "$PYTHON_VERSION" != "$RUST_VERSION" ]; then
echo "❌ Version mismatch!"
exit 1
fi
echo "✅ Versions in sync: $PYTHON_VERSION"

# Fast format & lint checks (parallel)
# Lint, format, type check (runs on all events)
quick-check:
name: Format & Lint
runs-on: ubuntu-latest
runs-on: cachekit
timeout-minutes: 10
steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v3
with:
enable-cache: true

- name: Set up Python
run: uv python install ${{ env.DEFAULT_PYTHON_VERSION }}

- name: Set up Rust
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- uses: actions/checkout@v6

- name: Cache Rust dependencies
uses: Swatinem/rust-cache@v2
with:
workspaces: rust

- name: Cache Python virtual environment
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: .venv
key: venv-${{ runner.os }}-py${{ env.DEFAULT_PYTHON_VERSION }}-${{ hashFiles('**/pyproject.toml', '**/uv.lock', 'rust/**/*.rs', 'rust/**/Cargo.toml') }}
restore-keys: |
venv-${{ runner.os }}-py${{ env.DEFAULT_PYTHON_VERSION }}-

- name: Install dependencies (if not cached)
run: |
uv sync --group dev
run: uv sync --group dev

- name: Check Python formatting
run: |
uv run ruff format --check .
run: uv run ruff format --check .

- name: Lint Python
if: success() || failure()
run: |
uv run ruff check .

- name: Scan Python dependencies for CVEs
if: success() || failure()
run: |
uv run pip-audit --desc
run: uv run ruff check .

- name: Type check Python
if: success() || failure()
run: |
uv run basedpyright --level error
run: uv run basedpyright --level error

- name: Check Rust formatting
if: success() || failure()
run: |
cd rust && cargo fmt --check
run: cd rust && cargo fmt --check

- name: Lint Rust
if: success() || failure()
run: |
cd rust && cargo clippy -- -D warnings
run: cd rust && cargo clippy -- -D warnings

# Critical test suite (parallel matrix)
# Push to main: runs on lab self-hosted runners (consistent perf, free minutes)
# PRs: runs on GitHub-hosted runners (untrusted code never touches lab infra)
test-critical:
# PR: single Python version, critical tests only
# Push: full matrix, full test suite
test:
name: Tests (Python ${{ matrix.python-version }})
runs-on: ${{ github.event_name == 'push' && 'cachekit' || 'ubuntu-latest' }}
runs-on: cachekit
timeout-minutes: 15
permissions:
contents: read
id-token: write # Required for Codecov OIDC
strategy:
fail-fast: false
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
python-version: ${{ github.event_name == 'push' && fromJSON('["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]') || fromJSON('["3.12"]') }}

services:
redis:
image: redis:7-alpine
image: redis@sha256:4bfd9eca23339865dc14fe75f6d9ae643f714924623978dd2798f1a673b08f43 # redis:7-alpine amd64
credentials:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
ports:
- 6379:6379
options: >-
Expand All @@ -138,35 +94,23 @@ jobs:
--health-retries 5

steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v3
with:
enable-cache: true

- name: Set up Python
run: uv python install ${{ matrix.python-version }}

- name: Set up Rust
uses: dtolnay/rust-toolchain@stable
- uses: actions/checkout@v6

- name: Cache Rust dependencies
uses: Swatinem/rust-cache@v2
with:
workspaces: rust

- name: Cache Python virtual environment
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: .venv
key: venv-${{ runner.os }}-py${{ matrix.python-version }}-${{ hashFiles('**/pyproject.toml', '**/uv.lock', 'rust/**/*.rs', 'rust/**/Cargo.toml') }}
restore-keys: |
venv-${{ runner.os }}-py${{ matrix.python-version }}-

- name: Install dependencies (if not cached)
run: |
uv sync --group dev
run: uv sync --python ${{ matrix.python-version }} --group dev

- name: Run critical tests (PRs)
if: github.event_name == 'pull_request'
Expand Down Expand Up @@ -212,60 +156,50 @@ jobs:
flags: ${{ github.event_name == 'push' && 'full' || 'critical' }}-python-${{ matrix.python-version }}
fail_ci_if_error: false

# Markdown documentation tests
test-docs:
name: Documentation Examples
runs-on: ubuntu-latest
# Version sync + doc tests (push to main only)
post-merge:
name: Post-Merge Checks
if: github.event_name == 'push'
runs-on: cachekit
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- name: Install uv
uses: astral-sh/setup-uv@v3
with:
enable-cache: true

- name: Set up Python
run: uv python install ${{ env.DEFAULT_PYTHON_VERSION }}

- name: Set up Rust
uses: dtolnay/rust-toolchain@stable
- name: Check Python ↔ Rust version consistency
run: |
PYTHON_VERSION=$(grep -E "^version = " pyproject.toml | head -1 | cut -d'"' -f2)
RUST_VERSION=$(grep -E "^version = " rust/Cargo.toml | head -1 | cut -d'"' -f2)
if [ "$PYTHON_VERSION" != "$RUST_VERSION" ]; then
echo "Version mismatch: Python=$PYTHON_VERSION Rust=$RUST_VERSION"
exit 1
fi

- name: Cache Rust dependencies
uses: Swatinem/rust-cache@v2
with:
workspaces: rust

- name: Cache Python virtual environment
uses: actions/cache@v4
with:
path: .venv
key: venv-${{ runner.os }}-py${{ env.DEFAULT_PYTHON_VERSION }}-${{ hashFiles('**/pyproject.toml', '**/uv.lock', 'rust/**/*.rs', 'rust/**/Cargo.toml') }}
restore-keys: |
venv-${{ runner.os }}-py${{ env.DEFAULT_PYTHON_VERSION }}-

- name: Install dependencies (if not cached)
run: |
uv sync --group dev
run: uv sync --group dev

- name: Scan Python dependencies for CVEs
run: uv run pip-audit --desc

- name: Run markdown documentation tests
run: |
make test-docs-examples
run: make test-docs-examples

# Summary job (required for branch protection)
ci-success:
name: CI Success
runs-on: ubuntu-latest
needs: [version-check, quick-check, test-critical, test-docs]
needs: [quick-check, test]
if: always()
steps:
- name: Check all jobs succeeded
run: |
if [[ "${{ needs.version-check.result }}" != "success" ]] || \
[[ "${{ needs.quick-check.result }}" != "success" ]] || \
[[ "${{ needs.test-critical.result }}" != "success" ]] || \
[[ "${{ needs.test-docs.result }}" != "success" ]]; then
echo "❌ One or more jobs failed"
if [[ "${{ needs.quick-check.result }}" != "success" ]] || \
[[ "${{ needs.test.result }}" != "success" ]]; then
echo "One or more jobs failed"
exit 1
fi
echo "All CI checks passed"
echo "All CI checks passed"
Loading