netspeed-cli 0.10.3

Command-line interface for testing internet bandwidth using speedtest.net
Documentation
name: CI

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

# Cancel redundant workflow runs on successive pushes to the same PR
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

env:
  CARGO_TERM_COLOR: always

jobs:
  test:
    name: Test (${{ matrix.os }})
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable
      - name: Cache dependencies
        uses: Swatinem/rust-cache@v2
      - name: Run tests
        run: cargo test --verbose
      - name: Build release
        run: cargo build --release

  socket-integration:
    name: Socket Integration Tests
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable
      - name: Cache dependencies
        uses: Swatinem/rust-cache@v2
      - name: Run mock socket tests
        run: cargo test --test mock_network_test -- --ignored --nocapture
      - name: Run upload integration tests
        run: cargo test --test integration_upload_fetch_test -- --ignored --nocapture
      - name: Run end-to-end socket tests
        run: cargo test --test e2e_test -- --ignored --nocapture

  clippy:
    name: Clippy
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy
      - name: Cache dependencies
        uses: Swatinem/rust-cache@v2
      - name: Run clippy
        run: cargo clippy --all-targets --all-features -- -D warnings

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

  doc:
    name: Documentation
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable
      - name: Cache dependencies
        uses: Swatinem/rust-cache@v2
      - name: Check documentation
        env:
          RUSTDOCFLAGS: -D warnings
        run: cargo doc --no-deps --workspace
      - name: Run doctests
        run: cargo test --doc

  audit:
    name: Security Audit (cargo-deny)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable
      - name: Cache dependencies
        uses: Swatinem/rust-cache@v2
      - name: Install cargo-deny
        uses: taiki-e/install-action@cargo-deny
      - name: Run cargo-deny (advisories, licenses, bans, sources)
        run: cargo deny check

  package:
    name: Package Validation
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable
      - name: Cache dependencies
        uses: Swatinem/rust-cache@v2
      - name: Verify crate package
        run: cargo package --locked

  msrv:
    name: MSRV Check
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@master
        with:
          toolchain: 1.86
      - name: Cache dependencies
        uses: Swatinem/rust-cache@v2
      - name: Verify build with MSRV
        run: cargo check

  benchmarks:
    name: Benchmark Regression Check
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 0  # Need full history for baseline comparison
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: llvm-tools-preview
      - name: Cache dependencies
        uses: Swatinem/rust-cache@v2
      - name: Install cargo-benchcmp
        run: cargo install cargo-benchcmp
      - name: Run benchmarks
        run: |
          cargo bench 2>&1 | tee benchmark_results.txt
      - name: Fetch baseline from main
        run: |
          # Try to find baseline from main branch
          if git show main:benches/baseline.txt &>/dev/null; then
            echo "Found baseline on main branch"
            git show main:benches/baseline.txt > benches/baseline.txt
          else
            echo "No baseline found on main - this may be the first run"
          fi
      - name: Check for regressions
        run: |
          # Skip comparison if baseline is placeholder
          if grep -q "PLACEHOLDER" benches/baseline.txt 2>/dev/null; then
            echo "Baseline is placeholder. Skipping regression check on first run."
          elif [ -f benches/baseline.txt ]; then
            echo "Comparing against baseline..."
            cargo benchcmp benches/baseline.txt benchmark_results.txt 2>&1 || {
              echo "Benchmark regression detected!"
              exit 1
            }
          else
            echo "No baseline to compare against. Skipping regression check."
          fi
      - name: Store baseline
        if: github.event_name == 'push' && github.ref == 'refs/heads/main'
        run: |
          echo "Updating benchmark baseline on main branch..."
          mkdir -p benches
          cp benchmark_results.txt benches/baseline.txt
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add benches/baseline.txt
          git commit -m "perf: update benchmark baseline" || echo "No changes to commit"


  coverage:
    name: Code Coverage
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: llvm-tools-preview
      - name: Cache dependencies
        uses: Swatinem/rust-cache@v2
      - name: Install cargo-llvm-cov
        run: cargo install cargo-llvm-cov
      - name: Generate coverage
        run: cargo llvm-cov --all-features --lcov --output-path lcov.info --fail-under-lines=83
      - name: Print coverage summary
        run: cargo llvm-cov --all-features --summary-only
      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v6
        with:
          files: lcov.info
          verbose: true
        env:
          CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

  brew-audit:
    name: Homebrew Audit
    runs-on: macos-latest
    # Only run when a version tag is pushed (release tarball must exist)
    if: startsWith(github.ref, 'refs/tags/v')
    steps:
      - uses: actions/checkout@v6
      - name: Audit formula
        run: |
          TAP_DIR="$(brew --repository)/Library/Taps/mapleDevJS/homebrew-netspeed-cli"
          mkdir -p "$TAP_DIR"
          cp netspeed-cli.rb "$TAP_DIR/"
          cd "$TAP_DIR"
          git init
          git add netspeed-cli.rb
          git commit -m "audit"
          brew install --build-from-source mapleDevJS/homebrew-netspeed-cli/netspeed-cli
          brew audit --strict --online mapleDevJS/homebrew-netspeed-cli/netspeed-cli