Skip to content

test: fix async coroutine warnings and CI compatibility #26

test: fix async coroutine warnings and CI compatibility

test: fix async coroutine warnings and CI compatibility #26

Workflow file for this run

name: CI
on:
push:
branches: [main, classic-next, develop]
pull_request:
branches: [main, classic-next, develop]
env:
RUST_BACKTRACE: 1
CARGO_TERM_COLOR: always
# Python version must match project requirements
PYTHON_VERSION: "3.12"
jobs:
# Formatting checks (non-blocking, aesthetic only)
format-python:
name: Python Formatting (Ruff)
runs-on: windows-latest
timeout-minutes: 10
continue-on-error: true # Don't block CI on formatting issues
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v4
- name: Set up Python
run: uv python install ${{ env.PYTHON_VERSION }}
- name: Install dependencies
run: uv sync --all-extras
- name: Run ruff format check
run: uv run ruff format --check .
format-rust:
name: Rust Formatting (rustfmt)
runs-on: windows-latest
timeout-minutes: 10
continue-on-error: true # Don't block CI on formatting issues
steps:
- uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- name: Run rustfmt check
run: cargo fmt --all --manifest-path rust/Cargo.toml -- --check
# Linting checks (can catch real bugs, run in parallel with builds)
lint-python:
name: Python Linting (Ruff)
runs-on: windows-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v4
- name: Set up Python
run: uv python install ${{ env.PYTHON_VERSION }}
- name: Install dependencies
run: uv sync --all-extras
- name: Run ruff check
run: uv run ruff check .
lint-rust:
name: Rust Linting (Clippy)
runs-on: windows-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: ~/.cargo/registry
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-registry-
- name: Cache cargo index
uses: actions/cache@v4
with:
path: ~/.cargo/git
key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-index-
- name: Cache cargo build
uses: actions/cache@v4
with:
path: rust/target
key: ${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }}-${{ hashFiles('rust/**/*.rs') }}
restore-keys: |
${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }}-
${{ runner.os }}-cargo-build-
- name: Run clippy
run: cargo clippy --workspace --all-targets --all-features --manifest-path rust/Cargo.toml -- -D warnings
# Build Rust components (no dependencies - runs immediately)
build-rust:
name: Build Rust Components
runs-on: windows-latest
timeout-minutes: 60 # Prevent build from hanging
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: ~/.cargo/registry
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-registry-
- name: Cache cargo index
uses: actions/cache@v4
with:
path: ~/.cargo/git
key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-index-
- name: Cache cargo build
uses: actions/cache@v4
with:
path: rust/target
key: ${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }}-${{ hashFiles('rust/**/*.rs') }}
restore-keys: |
${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }}-
${{ runner.os }}-cargo-build-
- name: Build all Rust workspace
run: cargo build --workspace --release --manifest-path rust/Cargo.toml
- name: Run Rust tests
run: |
# Run most tests in parallel
cargo test --workspace --release --manifest-path rust/Cargo.toml --exclude classic-gui-slint
# Run Slint GUI tests single-threaded to avoid Windows heap corruption during cleanup
# (all tests pass; crash only occurs during multi-threaded process shutdown)
cargo test --release --manifest-path rust/Cargo.toml -p classic-gui-slint -- --test-threads=1
- name: Upload Rust build artifacts
uses: actions/upload-artifact@v4
with:
name: rust-libs
path: |
rust/target/release/*.dll
rust/target/release/*.pdb
retention-days: 1
# Build Python bindings with maturin
build-python-bindings:
name: Build Python Bindings
runs-on: windows-latest
needs: [build-rust] # Only wait for Rust build, not linting
timeout-minutes: 30 # Maturin builds can be slow
steps:
- uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Install uv
uses: astral-sh/setup-uv@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: ~/.cargo/registry
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-registry-
- name: Cache cargo build
uses: actions/cache@v4
with:
path: rust/target
key: ${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }}-${{ hashFiles('rust/**/*.rs') }}
restore-keys: |
${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }}-
${{ runner.os }}-cargo-build-
- name: Install maturin
run: uv tool install maturin
- name: Build all Python bindings
run: |
$ErrorActionPreference = "Stop"
# Map binding names to their directory paths
# Most are in python-bindings/, but classic-shared-py is in foundation/
$bindingPaths = @{
"classic-shared-py" = "rust/foundation/classic-shared-py"
"classic-yaml-py" = "rust/python-bindings/classic-yaml-py"
"classic-database-py" = "rust/python-bindings/classic-database-py"
"classic-file-io-py" = "rust/python-bindings/classic-file-io-py"
"classic-scanlog-py" = "rust/python-bindings/classic-scanlog-py"
"classic-config-py" = "rust/python-bindings/classic-config-py"
"classic-registry-py" = "rust/python-bindings/classic-registry-py"
"classic-perf-py" = "rust/python-bindings/classic-perf-py"
"classic-pybridge-py" = "rust/python-bindings/classic-pybridge-py"
"classic-settings-py" = "rust/python-bindings/classic-settings-py"
"classic-message-py" = "rust/python-bindings/classic-message-py"
"classic-path-py" = "rust/python-bindings/classic-path-py"
"classic-constants-py" = "rust/python-bindings/classic-constants-py"
"classic-version-py" = "rust/python-bindings/classic-version-py"
"classic-resource-py" = "rust/python-bindings/classic-resource-py"
"classic-xse-py" = "rust/python-bindings/classic-xse-py"
"classic-web-py" = "rust/python-bindings/classic-web-py"
"classic-scangame-py" = "rust/python-bindings/classic-scangame-py"
"classic-update-py" = "rust/python-bindings/classic-update-py"
}
# Build in dependency order (classic-shared-py first)
$buildOrder = @(
"classic-shared-py",
"classic-yaml-py",
"classic-database-py",
"classic-file-io-py",
"classic-scanlog-py",
"classic-config-py",
"classic-registry-py",
"classic-perf-py",
"classic-pybridge-py",
"classic-settings-py",
"classic-message-py",
"classic-path-py",
"classic-constants-py",
"classic-version-py",
"classic-resource-py",
"classic-xse-py",
"classic-web-py",
"classic-scangame-py",
"classic-update-py"
)
foreach ($binding in $buildOrder) {
$path = $bindingPaths[$binding]
Write-Host "Building $binding from $path..."
Set-Location $path
maturin build --release --out dist
if ($LASTEXITCODE -ne 0) {
throw "Failed to build $binding"
}
Set-Location $env:GITHUB_WORKSPACE
}
shell: pwsh
- name: Upload Python binding wheels
uses: actions/upload-artifact@v4
with:
name: python-wheels
path: |
rust/python-bindings/*/dist/*.whl
rust/foundation/*/dist/*.whl
retention-days: 1
# Run Python tests
test-python:
name: Python Tests
runs-on: windows-latest
needs: [build-python-bindings]
timeout-minutes: 30 # Kill job if tests hang for 30 minutes
env:
PYTHONTRACEMALLOC: "10" # Enable tracemalloc with 10 frame depth for better error reporting
steps:
- uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Install uv
uses: astral-sh/setup-uv@v4
- name: Set up Python
run: uv python install ${{ env.PYTHON_VERSION }}
- name: Download Python binding wheels
uses: actions/download-artifact@v4
with:
name: python-wheels
path: wheels
- name: Install Python dependencies and wheels
run: |
uv sync --all-extras
# Debug: Show workflow version
Write-Host "CI workflow version: 2024-11-27-v6"
# Install pip into the venv so we can use it directly
uv pip install pip
# List downloaded wheels for debugging
Write-Host "Downloaded wheels:"
$wheels = Get-ChildItem -Path wheels -Filter *.whl -Recurse
$wheels | ForEach-Object { Write-Host " $($_.FullName)" }
Write-Host "Total wheels found: $($wheels.Count)"
# Install each wheel using pip directly (bypasses uv lockfile)
foreach ($wheel in $wheels) {
Write-Host "Installing: $($wheel.Name)"
uv run python -m pip install $wheel.FullName --force-reinstall --no-deps
}
# Verify installation
Write-Host "Installed packages (classic*):"
uv run python -m pip list | Select-String "classic"
shell: pwsh
- name: Verify Rust acceleration
run: |
uv run python -c "import classic_yaml; print(f'Rust YAML version: {classic_yaml.__version__}')"
uv run python -c "from ClassicLib.integration.status import print_rust_status; print_rust_status()"
- name: Run pytest (unit tests)
run: uv run pytest -m "unit and not slow" --maxfail=5 --tb=short --timeout=300 --timeout-method=thread
timeout-minutes: 10
- name: Run pytest (integration tests)
run: uv run pytest -m "integration" --maxfail=3 --tb=short --timeout=600 --timeout-method=thread
timeout-minutes: 15
- name: Run pytest (Rust integration tests)
run: uv run pytest tests/rust_integration/ -v --maxfail=3 --tb=short --timeout=300 --timeout-method=thread
timeout-minutes: 10
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: pytest-results
path: |
.pytest_cache/
test-results/
retention-days: 7