name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
jobs:
fmt:
name: Format Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- name: Check formatting
run: cargo fmt -- --check
clippy:
name: Clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- name: Cache
uses: Swatinem/rust-cache@v2
- name: Run Clippy
run: cargo clippy --all-targets -- -D warnings
test:
name: Test
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
rust: [stable, beta]
include:
- os: ubuntu-latest
rust: nightly
steps:
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
- name: Cache
uses: Swatinem/rust-cache@v2
with:
key: ${{ matrix.rust }}
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose
- name: Build documentation
run: cargo doc --no-deps
features:
name: Feature Tests
runs-on: ubuntu-latest
strategy:
matrix:
features:
- ""
- "python"
steps:
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache
uses: Swatinem/rust-cache@v2
- name: Build with features
run: cargo build --no-default-features --features "${{ matrix.features }}"
if: matrix.features != ''
- name: Build without features
run: cargo build --no-default-features
if: matrix.features == ''
cli:
name: CLI Build
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache
uses: Swatinem/rust-cache@v2
- name: Build CLI
run: cargo build -p pdf_oxide_cli
- name: Test CLI --help
run: cargo run -p pdf_oxide_cli -- --help
mcp:
name: MCP Build
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache
uses: Swatinem/rust-cache@v2
- name: Build MCP server
run: cargo build -p pdf_oxide_mcp
python:
name: Python Bindings
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache
uses: Swatinem/rust-cache@v2
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Create virtual environment
run: uv venv
- name: Install maturin
run: uv pip install maturin pytest
- name: Build Python package
run: uv run maturin build --release --features python --out dist
- name: Install wheel
shell: bash
run: |
wheel=$(ls dist/*.whl)
uv pip install "$wheel"
- name: Test Python bindings
run: uv run python -c "import pdf_oxide; print(pdf_oxide.VERSION)"
wasm-build:
name: WASM Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-unknown
- name: Cache
uses: Swatinem/rust-cache@v2
- name: Build WASM
run: cargo build --lib --target wasm32-unknown-unknown --features wasm
coverage:
name: Code Coverage
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' || github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
components: llvm-tools-preview
- name: Cache
uses: Swatinem/rust-cache@v2
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@cargo-llvm-cov
- name: Generate coverage
run: |
cargo llvm-cov --lib --tests --lcov --output-path lcov.info \
--ignore-filename-regex '(cid_mappings/|adobe_glyph_list\.rs|python\.rs|wasm\.rs|ocr/|rendering/|bin/)'
- name: Enforce 85% coverage threshold
run: |
cargo llvm-cov report \
--ignore-filename-regex '(cid_mappings/|adobe_glyph_list\.rs|python\.rs|wasm\.rs|ocr/|rendering/|bin/)' \
--fail-under-lines 85
- name: Upload coverage to Codecov
if: always()
uses: codecov/codecov-action@v6
with:
files: ./lcov.info
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: false
bench:
name: Benchmark Smoke Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache
uses: Swatinem/rust-cache@v2
- name: Build benchmarks
run: cargo bench --no-run
audit:
name: Security Audit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Run security audit
uses: actions-rust-lang/audit@v1
deny:
name: Dependency Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Check dependencies
uses: EmbarkStudios/cargo-deny-action@v2
with:
arguments: ""