feat: type optimizations - semantic newtypes and boxed namespace metadata #168
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| permissions: | |
| contents: read | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| types: [opened, synchronize, reopened, labeled] | |
| branches: [main] | |
| env: | |
| CARGO_TERM_COLOR: always | |
| CARGO_INCREMENTAL: 0 | |
| CARGO_NET_RETRY: 10 | |
| RUST_BACKTRACE: short | |
| RUSTFLAGS: "-D warnings" | |
| RUSTUP_MAX_RETRIES: 10 | |
| # Cancel previous runs on new push | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| # Detect which files changed to optimize CI | |
| changes: | |
| name: Detect Changes | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| outputs: | |
| rust-core: ${{ steps.filter.outputs.rust-core }} | |
| python: ${{ steps.filter.outputs.python }} | |
| node: ${{ steps.filter.outputs.node }} | |
| ci: ${{ steps.filter.outputs.ci }} | |
| docs: ${{ steps.filter.outputs.docs }} | |
| full-ci: ${{ steps.check-full-ci.outputs.full-ci }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Check for full-ci label or main branch push | |
| id: check-full-ci | |
| run: | | |
| FULL_CI="false" | |
| # Always run full CI on main branch pushes | |
| if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" == "refs/heads/main" ]]; then | |
| FULL_CI="true" | |
| echo "Full CI enabled: main branch push" | |
| fi | |
| # Check for full-ci label on PRs | |
| if [[ "${{ github.event_name }}" == "pull_request" ]]; then | |
| if [[ "${{ contains(github.event.pull_request.labels.*.name, 'full-ci') }}" == "true" ]]; then | |
| FULL_CI="true" | |
| echo "Full CI enabled: full-ci label present" | |
| fi | |
| fi | |
| echo "full-ci=$FULL_CI" >> $GITHUB_OUTPUT | |
| - uses: dorny/paths-filter@v3 | |
| id: filter | |
| with: | |
| filters: | | |
| rust-core: | |
| - 'crates/feedparser-rs-core/**' | |
| - 'Cargo.toml' | |
| - 'Cargo.lock' | |
| python: | |
| - 'crates/feedparser-rs-py/**' | |
| - 'crates/feedparser-rs-core/**' | |
| - 'Cargo.toml' | |
| - 'Cargo.lock' | |
| node: | |
| - 'crates/feedparser-rs-node/**' | |
| - 'crates/feedparser-rs-core/**' | |
| - 'Cargo.toml' | |
| - 'Cargo.lock' | |
| ci: | |
| - '.github/workflows/**' | |
| - 'deny.toml' | |
| - 'rustfmt.toml' | |
| - 'Makefile.toml' | |
| docs: | |
| - '**/*.md' | |
| - 'docs/**' | |
| # Fast checks - run in parallel, fail fast | |
| lint-stable: | |
| name: Lint (clippy + docs) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Install Rust stable | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: clippy | |
| - name: Cache Cargo | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| shared-key: "lint-stable" | |
| save-if: ${{ github.ref == 'refs/heads/main' }} | |
| - name: Install cargo-make | |
| uses: taiki-e/install-action@cargo-make | |
| - name: Run clippy and doc checks | |
| run: cargo make ci-lint-stable | |
| lint-nightly: | |
| name: Lint (rustfmt) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Install Rust nightly | |
| uses: dtolnay/rust-toolchain@nightly | |
| with: | |
| components: rustfmt | |
| - name: Check formatting | |
| run: cargo +nightly fmt --all -- --check | |
| # Security audit | |
| security: | |
| name: Security Audit | |
| needs: [changes] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| # Run on: Rust core changes, CI config changes, or full CI mode | |
| if: | | |
| needs.changes.outputs.rust-core == 'true' || | |
| needs.changes.outputs.ci == 'true' || | |
| needs.changes.outputs.full-ci == 'true' | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Install Rust stable | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Cache Cargo | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| shared-key: "security" | |
| - name: Install cargo-deny | |
| uses: taiki-e/install-action@cargo-deny | |
| - name: Run cargo-deny | |
| run: cargo deny check | |
| # Cross-platform Rust tests | |
| test-rust: | |
| name: Test Rust (${{ matrix.os }}) | |
| needs: [changes, lint-stable, lint-nightly] | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 30 | |
| # Run on: Rust core changes, CI config changes, or full CI mode | |
| if: | | |
| needs.changes.outputs.rust-core == 'true' || | |
| needs.changes.outputs.ci == 'true' || | |
| needs.changes.outputs.full-ci == 'true' | |
| permissions: | |
| contents: read | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest, windows-latest] | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Disable Windows Defender real-time monitoring | |
| if: runner.os == 'Windows' | |
| shell: powershell | |
| run: Set-MpPreference -DisableRealtimeMonitoring $true | |
| - name: Install Rust stable | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: llvm-tools | |
| - name: Cache Cargo | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| shared-key: "test-rust-${{ matrix.os }}" | |
| save-if: ${{ github.ref == 'refs/heads/main' }} | |
| - name: Install tools | |
| uses: taiki-e/install-action@v2 | |
| with: | |
| tool: cargo-make,cargo-nextest | |
| - name: Run Rust tests | |
| run: cargo make ci-test-rust | |
| # Python bindings tests | |
| test-python: | |
| name: Test Python (${{ matrix.os }} - Py${{ matrix.python }}) | |
| needs: [changes, lint-stable, lint-nightly] | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 20 | |
| # Run on: Python changes, CI config changes, or full CI mode | |
| if: | | |
| needs.changes.outputs.python == 'true' || | |
| needs.changes.outputs.ci == 'true' || | |
| needs.changes.outputs.full-ci == 'true' | |
| permissions: | |
| contents: read | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest, windows-latest] | |
| python: ['3.9', '3.10', '3.11', '3.12', '3.13'] | |
| exclude: | |
| # Reduce matrix size - test all versions on Linux only | |
| - os: macos-latest | |
| python: '3.9' | |
| - os: macos-latest | |
| python: '3.10' | |
| - os: macos-latest | |
| python: '3.11' | |
| - os: windows-latest | |
| python: '3.9' | |
| - os: windows-latest | |
| python: '3.10' | |
| - os: windows-latest | |
| python: '3.11' | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Disable Windows Defender real-time monitoring | |
| if: runner.os == 'Windows' | |
| shell: powershell | |
| run: Set-MpPreference -DisableRealtimeMonitoring $true | |
| - name: Install Rust stable | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: llvm-tools | |
| - name: Cache Cargo | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| shared-key: "test-python-${{ matrix.os }}" | |
| save-if: ${{ github.ref == 'refs/heads/main' }} | |
| workspaces: crates/feedparser-rs-py | |
| - name: Setup Python ${{ matrix.python }} | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: ${{ matrix.python }} | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v7 | |
| - name: Install cargo-make | |
| uses: taiki-e/install-action@cargo-make | |
| - name: Run Python tests | |
| run: cargo make ci-test-python | |
| # Node.js bindings tests | |
| test-node: | |
| name: Test Node.js (${{ matrix.os }} - Node ${{ matrix.node }}) | |
| needs: [changes, lint-stable, lint-nightly] | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 20 | |
| # Run on: Node changes, CI config changes, or full CI mode | |
| if: | | |
| needs.changes.outputs.node == 'true' || | |
| needs.changes.outputs.ci == 'true' || | |
| needs.changes.outputs.full-ci == 'true' | |
| permissions: | |
| contents: read | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest, windows-latest] | |
| node: ['20'] | |
| include: | |
| # Test Node 22 only on Linux | |
| - os: ubuntu-latest | |
| node: '22' | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Disable Windows Defender real-time monitoring | |
| if: runner.os == 'Windows' | |
| shell: powershell | |
| run: Set-MpPreference -DisableRealtimeMonitoring $true | |
| - name: Install Rust stable | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: llvm-tools | |
| - name: Cache Cargo | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| shared-key: "test-node-${{ matrix.os }}" | |
| save-if: ${{ github.ref == 'refs/heads/main' }} | |
| workspaces: crates/feedparser-rs-node | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 9 | |
| - name: Setup Node.js ${{ matrix.node }} | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: ${{ matrix.node }} | |
| cache: 'pnpm' | |
| cache-dependency-path: crates/feedparser-rs-node/pnpm-lock.yaml | |
| - name: Install cargo-make | |
| uses: taiki-e/install-action@cargo-make | |
| - name: Run Node.js tests | |
| run: cargo make ci-test-node | |
| # Rust code coverage | |
| coverage-rust: | |
| name: Rust Code Coverage | |
| needs: [changes, lint-stable, lint-nightly] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| # Run on: Rust core changes, CI config changes, or full CI mode | |
| if: | | |
| needs.changes.outputs.rust-core == 'true' || | |
| needs.changes.outputs.ci == 'true' || | |
| needs.changes.outputs.full-ci == 'true' | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Install Rust stable | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: llvm-tools | |
| - name: Cache Cargo | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| shared-key: "coverage-rust" | |
| save-if: ${{ github.ref == 'refs/heads/main' }} | |
| - name: Install tools | |
| uses: taiki-e/install-action@v2 | |
| with: | |
| tool: cargo-make,cargo-llvm-cov,cargo-nextest | |
| - name: Generate Rust coverage | |
| run: cargo make ci-coverage-rust | |
| - name: Upload to codecov | |
| uses: codecov/codecov-action@v5 | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| files: lcov.info | |
| fail_ci_if_error: true | |
| flags: rust-core | |
| verbose: true | |
| # Python code coverage | |
| coverage-python: | |
| name: Python Code Coverage | |
| needs: [changes, lint-stable, lint-nightly] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| # Run on: Python changes, CI config changes, or full CI mode | |
| if: | | |
| needs.changes.outputs.python == 'true' || | |
| needs.changes.outputs.ci == 'true' || | |
| needs.changes.outputs.full-ci == 'true' | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Install Rust stable | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Cache Cargo | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| shared-key: "coverage-python" | |
| workspaces: crates/feedparser-rs-py | |
| - name: Setup Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.12' | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v7 | |
| - name: Install cargo-make | |
| uses: taiki-e/install-action@cargo-make | |
| - name: Generate Python coverage | |
| run: cargo make ci-coverage-python | |
| continue-on-error: true | |
| - name: Upload Python coverage to codecov | |
| uses: codecov/codecov-action@v5 | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| files: crates/feedparser-rs-py/coverage.xml | |
| fail_ci_if_error: false | |
| flags: python-bindings | |
| if: hashFiles('crates/feedparser-rs-py/tests/*.py') != '' | |
| # Node.js code coverage | |
| coverage-node: | |
| name: Node.js Code Coverage | |
| needs: [changes, lint-stable, lint-nightly] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| # Run on: Node changes, CI config changes, or full CI mode | |
| if: | | |
| needs.changes.outputs.node == 'true' || | |
| needs.changes.outputs.ci == 'true' || | |
| needs.changes.outputs.full-ci == 'true' | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Install Rust stable | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Cache Cargo | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| shared-key: "coverage-node" | |
| workspaces: crates/feedparser-rs-node | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 9 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: '20' | |
| cache: 'pnpm' | |
| cache-dependency-path: crates/feedparser-rs-node/pnpm-lock.yaml | |
| - name: Install cargo-make | |
| uses: taiki-e/install-action@cargo-make | |
| - name: Generate Node.js coverage | |
| run: cargo make ci-coverage-node | |
| continue-on-error: true | |
| - name: Upload Node.js coverage to codecov | |
| uses: codecov/codecov-action@v5 | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| directory: crates/feedparser-rs-node/coverage | |
| fail_ci_if_error: false | |
| flags: node-bindings | |
| # MSRV check | |
| msrv: | |
| name: Check MSRV (1.88.0) | |
| needs: [changes, lint-stable, lint-nightly] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| # Run on: Rust core changes, CI config changes, or full CI mode | |
| if: | | |
| needs.changes.outputs.rust-core == 'true' || | |
| needs.changes.outputs.ci == 'true' || | |
| needs.changes.outputs.full-ci == 'true' | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Install Rust 1.88.0 | |
| uses: dtolnay/rust-toolchain@master | |
| with: | |
| toolchain: "1.88.0" | |
| - name: Cache Cargo | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| shared-key: "msrv" | |
| - name: Install cargo-make | |
| uses: taiki-e/install-action@cargo-make | |
| - name: Check with MSRV | |
| run: cargo make ci-msrv | |
| # All checks passed gate | |
| ci-success: | |
| name: CI Success | |
| needs: [changes, lint-stable, lint-nightly, security, test-rust, test-python, test-node, coverage-rust, coverage-python, coverage-node, msrv] | |
| runs-on: ubuntu-latest | |
| if: always() | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Check all jobs | |
| run: | | |
| # Helper function to check job result (success or skipped is OK) | |
| check_job() { | |
| local job_name=$1 | |
| local job_result=$2 | |
| if [[ "$job_result" == "success" ]] || [[ "$job_result" == "skipped" ]]; then | |
| echo "$job_name: $job_result ✓" | |
| return 0 | |
| else | |
| echo "$job_name: $job_result ✗" | |
| return 1 | |
| fi | |
| } | |
| # Track overall status | |
| FAILED=0 | |
| # Lint jobs must always succeed (never skipped) | |
| if [[ "${{ needs.lint-stable.result }}" != "success" ]]; then | |
| echo "lint-stable: ${{ needs.lint-stable.result }} ✗ (must succeed)" | |
| FAILED=1 | |
| else | |
| echo "lint-stable: ${{ needs.lint-stable.result }} ✓" | |
| fi | |
| if [[ "${{ needs.lint-nightly.result }}" != "success" ]]; then | |
| echo "lint-nightly: ${{ needs.lint-nightly.result }} ✗ (must succeed)" | |
| FAILED=1 | |
| else | |
| echo "lint-nightly: ${{ needs.lint-nightly.result }} ✓" | |
| fi | |
| # Check all other jobs (success or skipped is OK) | |
| check_job "security" "${{ needs.security.result }}" || FAILED=1 | |
| check_job "test-rust" "${{ needs.test-rust.result }}" || FAILED=1 | |
| check_job "test-python" "${{ needs.test-python.result }}" || FAILED=1 | |
| check_job "test-node" "${{ needs.test-node.result }}" || FAILED=1 | |
| check_job "coverage-rust" "${{ needs.coverage-rust.result }}" || FAILED=1 | |
| check_job "coverage-python" "${{ needs.coverage-python.result }}" || FAILED=1 | |
| check_job "coverage-node" "${{ needs.coverage-node.result }}" || FAILED=1 | |
| check_job "msrv" "${{ needs.msrv.result }}" || FAILED=1 | |
| if [[ $FAILED -eq 1 ]]; then | |
| echo "" | |
| echo "❌ One or more jobs failed" | |
| exit 1 | |
| fi | |
| echo "" | |
| echo "✅ All CI jobs passed successfully!" |