gpu-probe 0.1.0

Cross-platform GPU memory (VRAM) detection with no vendor SDKs — uses NVML, Linux DRM sysfs (AMD/Intel), and macOS system_profiler/sysctl.
Documentation
name: CI

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

env:
  CARGO_TERM_COLOR: always

jobs:
  test:
    name: Test
    runs-on: ubuntu-latest
    strategy:
      matrix:
        rust:
          - stable
          - beta
          - nightly

    steps:
      - uses: actions/checkout@v4

      - name: Install Rust
        uses: dtolnay/rust-toolchain@master
        with:
          toolchain: ${{ matrix.rust }}

      - name: Cache dependencies
        uses: actions/cache@v6
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}

      - name: Run tests
        run: cargo test --all-features --verbose

      - name: Run doctests
        run: cargo test --doc --all-features --verbose

  fmt:
    name: Rustfmt
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: rustfmt
      - name: Check formatting
        run: cargo fmt --all -- --check

  clippy:
    name: Clippy
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy
      - name: Cache dependencies
        uses: actions/cache@v6
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: ${{ runner.os }}-cargo-clippy-${{ hashFiles('**/Cargo.lock') }}
      - name: Run clippy
        run: cargo clippy --all-targets --all-features --workspace -- -W clippy::pedantic -D warnings -W unused_must_use

  coverage:
    name: Coverage
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: taiki-e/install-action@v2
        with:
          tool: just,cargo-llvm-cov

      - uses: dtolnay/rust-toolchain@stable
        with:
          components: llvm-tools-preview

      - name: Cache dependencies
        uses: actions/cache@v6
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: ${{ runner.os }}-cargo-llvmcov-${{ hashFiles('**/Cargo.lock') }}

      # Hosted runners have no GPU, so the NVML/DRM/Metal probes all take their
      # "nothing found" paths. What's measured is the parsing and formatting
      # logic plus those empty paths; the vendor query paths are only reachable
      # on real hardware and are intentionally not counted here.
      - name: Coverage (lcov + summary)
        run: just coverage-lcov

      - uses: actions/upload-artifact@v4
        with:
          name: coverage-lcov
          path: lcov.info

  # Publish the browsable HTML coverage report to GitHub Pages (main only).
  # One-time setup: repo Settings -> Pages -> Source = "GitHub Actions".
  # Served at https://jorge-menjivar.github.io/gpu-probe/.
  coverage-pages:
    name: Coverage (Pages)
    runs-on: ubuntu-latest
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    permissions:
      contents: read
      pages: write
      id-token: write
    # Serialize deploys; never cancel an in-flight Pages deployment.
    concurrency:
      group: pages
      cancel-in-progress: false
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
      - uses: actions/checkout@v4

      - uses: taiki-e/install-action@v2
        with:
          tool: just,cargo-llvm-cov

      - uses: dtolnay/rust-toolchain@stable
        with:
          components: llvm-tools-preview

      - name: Cache dependencies
        uses: actions/cache@v6
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: ${{ runner.os }}-cargo-llvmcov-${{ hashFiles('**/Cargo.lock') }}

      # Same scope as the lcov job: renders the static report to
      # target/llvm-cov/html/.
      - name: Generate HTML coverage report
        run: just coverage --html

      # Drop a shields.io "endpoint" JSON beside the HTML so the README badge
      # reads the latest line-% straight from Pages — no Gist or PAT. `report`
      # reuses the profiles just collected (no rebuild). jq ships on the runner.
      - name: Write coverage badge JSON
        run: |
          set -euo pipefail
          pct=$(cargo llvm-cov report --json --summary-only --ignore-filename-regex 'tests/' \
            | jq -r '.data[0].totals.lines.percent')
          if   awk "BEGIN{exit !($pct>=90)}"; then color=brightgreen
          elif awk "BEGIN{exit !($pct>=75)}"; then color=green
          elif awk "BEGIN{exit !($pct>=60)}"; then color=yellow
          elif awk "BEGIN{exit !($pct>=40)}"; then color=orange
          else color=red
          fi
          printf '{"schemaVersion":1,"label":"coverage","message":"%.1f%%","color":"%s"}\n' \
            "$pct" "$color" > target/llvm-cov/html/coverage.json
          cat target/llvm-cov/html/coverage.json

      - uses: actions/configure-pages@v5

      - uses: actions/upload-pages-artifact@v3
        with:
          path: target/llvm-cov/html

      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4

  package-size:
    name: Check Package Size
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable

      - name: Check package size
        run: |
          # Run dry-run and capture output
          echo "Checking package size..."
          DRY_RUN_OUTPUT=$(cargo publish --dry-run 2>&1)

          # Extract size from output (format: "Packaged X files, Y.ZKiB (A.BKiB compressed)")
          SIZE_LINE=$(echo "$DRY_RUN_OUTPUT" | grep -E "Packaged.*\([0-9.]+[KM]iB compressed\)")
          if [ -z "$SIZE_LINE" ]; then
            echo "Warning: Could not extract package size from output"
            echo "Dry run output:"
            echo "$DRY_RUN_OUTPUT"
          else
            # Extract compressed size (the one in parentheses)
            COMPRESSED_SIZE=$(echo "$SIZE_LINE" | sed -E 's/.*\(([0-9.]+)([KM])iB compressed\).*/\1 \2/')
            SIZE_NUM=$(echo "$COMPRESSED_SIZE" | cut -d' ' -f1)
            SIZE_UNIT=$(echo "$COMPRESSED_SIZE" | cut -d' ' -f2)

            echo "Package compressed size: ${SIZE_NUM}${SIZE_UNIT}iB"

            # Convert to KB for comparison
            if [ "$SIZE_UNIT" = "M" ]; then
              SIZE_KB=$(echo "$SIZE_NUM * 1024" | bc | cut -d. -f1)
            else
              SIZE_KB=$(echo "$SIZE_NUM" | cut -d. -f1)
            fi

            # Check if size exceeds 50KB
            if [ "$SIZE_KB" -gt 50 ]; then
              echo "::warning::Package size (${SIZE_KB}KB) exceeds 50KB limit"
              echo "Consider:"
              echo "  - Reviewing included files in Cargo.toml"
              echo "  - Checking for unnecessary test assets"
              echo "  - Using exclude patterns for large files"
              # Don't fail CI, just warn
            else
              echo "✓ Package size check passed (${SIZE_KB}KB <= 50KB)"
            fi
          fi