diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..fd46f9e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,132 @@ +# +# CI — Tests & Checks +# +# Runs on every push and every PR. +# - Lint (cargo clippy + rustfmt) +# - Rust unit tests (cargo test) +# - Python integration tests (pytest) +# - Build check (maturin build) +# + +name: CI + +on: + push: + branches: ["master", "main"] + pull_request: + branches: ["master", "main"] + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + +jobs: + # Rust lint + fmt + rust-lint: + name: Rust Lint & Fmt + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + components: clippy, rustfmt + + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@v2 + + - name: Check formatting + run: cargo fmt --all -- --check + + - name: Clippy (warnings as errors) + run: cargo clippy --all-targets --all-features -- -D warnings + + # Rust unit tests + rust-tests: + name: Rust Tests + runs-on: ubuntu-latest + needs: rust-lint + steps: + - uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@v2 + + - name: Run Rust tests + run: cargo test --all-features + + # Python integration tests + python-tests: + name: Python Tests (Python ${{ matrix.python }}) + runs-on: ubuntu-latest + needs: rust-lint + strategy: + fail-fast: false + matrix: + python: ["3.10", "3.11", "3.12", "3.13", "3.14"] + steps: + - uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@v2 + + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + + - name: Set up Python ${{ matrix.python }} + run: uv python install ${{ matrix.python }} + + - name: Install dependencies + run: uv sync --all-extras --dev + + - name: Build and install Ryx + run: uv run maturin develop --release + + - name: Run pytest + run: uv run pytest tests/ -v --tb=short + + - name: Run examples smoke test + run: | + for f in examples/*.py; do + echo "Running $f ..." + uv run python "$f" + done + + # Maturin build check + build-check: + name: Build Check (wheel) + runs-on: ubuntu-latest + needs: [rust-tests, python-tests] + steps: + - uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@v2 + + - name: Install uv + uses: astral-sh/setup-uv@v5 + + - name: Build wheel + uses: PyO3/maturin-action@v1 + with: + command: build + args: --release --out dist + manylinux: auto + + - name: Upload wheel artifact + uses: actions/upload-artifact@v4 + with: + name: wheels-linux + path: dist/ diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..7ff1961 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,68 @@ +# +# Docs — Build & Deploy Docusaurus +# +# Builds the Docusaurus documentation and deploys to GitHub Pages. +# Triggers on pushes to the docs/ directory or the master branch. +# Also available as a manual dispatch. +# + +name: Docs + +on: + push: + branches: ["master", "main"] + paths: + - "docs/**" + - ".github/workflows/docs.yml" + workflow_dispatch: + +# Grant GITHUB_TOKEN permission to deploy to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Only one deployment at a time; auto-cancel in-progress runs +concurrency: + group: pages + cancel-in-progress: true + +jobs: + build: + name: Build Docusaurus Docs + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: npm + cache-dependency-path: docs/package-lock.json + + - name: Install dependencies + working-directory: docs + run: npm ci + + - name: Build docs + working-directory: docs + run: npm run build + + - name: Upload build artifact + uses: actions/upload-pages-artifact@v3 + with: + path: docs/build + + deploy: + name: Deploy to GitHub Pages + runs-on: ubuntu-latest + needs: build + if: github.event_name != 'pull_request' + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..e8949fa --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,165 @@ +# +# Release — Build Wheels & Publish to PyPI +# +# Triggered automatically when a Git tag is pushed (e.g. v0.1.0). +# Builds wheels for all major platforms via Maturin's manylinux Docker images +# and native macOS/Windows runners, then publishes to PyPI. +# +# Tag format: v* (e.g. v0.1.0, v1.0.0, v0.2.0-beta.1) +# + +name: Release + +on: + push: + tags: + - "v*" + workflow_dispatch: + +env: + CARGO_TERM_COLOR: always + +jobs: + # Build wheels for all platforms + build-wheels: + name: Build Wheels — ${{ matrix.os }} (${{ matrix.target }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + # Linux (manylinux) — x86_64 + - os: ubuntu-latest + target: x86_64-unknown-linux-gnu + manylinux: auto + # Linux (manylinux) — aarch64 + - os: ubuntu-latest + target: aarch64-unknown-linux-gnu + manylinux: auto + # macOS — x86_64 (Intel) + - os: macos-13 + target: x86_64-apple-darwin + manylinux: auto + # macOS — aarch64 (Apple Silicon) + - os: macos-latest + target: aarch64-apple-darwin + manylinux: auto + # Windows — x86_64 + - os: windows-latest + target: x86_64-pc-windows-msvc + manylinux: auto + + steps: + - uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@v2 + + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.target }} + args: --release --out dist --interpreter 3.10 3.11 3.12 3.13 3.14 + manylinux: ${{ matrix.manylinux }} + rust-toolchain: stable + + - name: Upload wheel artifact + uses: actions/upload-artifact@v4 + with: + name: wheels-${{ matrix.os }}-${{ matrix.target }} + path: dist/ + + # Build source distribution + build-sdist: + name: Build Source Distribution + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@v2 + + - name: Build sdist + uses: PyO3/maturin-action@v1 + with: + command: sdist + args: --out dist + + - name: Upload sdist artifact + uses: actions/upload-artifact@v4 + with: + name: sdist + path: dist/ + + # Create GitHub Release + github-release: + name: Create GitHub Release + runs-on: ubuntu-latest + needs: [build-wheels, build-sdist] + if: startsWith(github.ref, 'refs/tags/') + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: dist/ + pattern: wheels-* + merge-multiple: true + + - name: Download sdist + uses: actions/download-artifact@v4 + with: + name: sdist + path: dist/ + + - name: Create GitHub Release + env: + GH_TOKEN: ${{ github.token }} + run: | + TAG_NAME="${GITHUB_REF#refs/tags/}" + gh release create "$TAG_NAME" dist/* \ + --title "Ryx $TAG_NAME" \ + --generate-notes \ + --draft + + # Publish to PyPI + publish-pypi: + name: Publish to PyPI + runs-on: ubuntu-latest + needs: [build-wheels, build-sdist] + if: startsWith(github.ref, 'refs/tags/') + environment: + name: pypi + url: https://pypi.org/p/ryx + permissions: + id-token: write # OIDC token for trusted publishing + steps: + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: dist/ + pattern: wheels-* + merge-multiple: true + + - name: Download sdist + uses: actions/download-artifact@v4 + with: + name: sdist + path: dist/ + + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + packages-dir: dist/ + skip-existing: true