fetter 3.3.0

System-wide Python package discovery, validation, vulnerability scanning, and allow-listing.
Documentation
name: CI

on:
  pull_request:
  push:
  release:
    types: published

jobs:
  #-----------------------------------------------------------------------------
  build-and-test:
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest] #, windows-latest]
        rust: ["1.85.0", stable]
        python: ["3.9", "3.14"]

    name: Build & Test / ${{ matrix.os }} / Rust ${{ matrix.rust }} / Python ${{ matrix.python }}
    runs-on: ${{ matrix.os }}

    steps:
      - uses: actions/checkout@v6

      - name: Git commit info
        run: git log -n 1

      - name: Install Python ${{ matrix.python }}
        uses: actions/setup-python@master
        with:
          python-version: ${{ matrix.python }}

      - name: Install Rust ${{ matrix.rust }}
        uses: actions-rust-lang/setup-rust-toolchain@v1
        with:
          toolchain: ${{ matrix.rust }}

      - name: Build
        run: cargo build --verbose

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

      - name: Build auditable
        if: matrix.rust == 'stable' && matrix.os == 'ubuntu-latest' && matrix.python == '3.14'
        run: | # https://github.com/rust-secure-code/cargo-auditable#usage
          cargo install cargo-auditable cargo-audit
          cargo auditable build

      - name: Fail auditable build on high/critical vulnerabilities
        if: matrix.rust == 'stable' && matrix.os == 'ubuntu-latest' && matrix.python == '3.14'
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: rootfs # https://trivy.dev/latest/docs/coverage/language/rust/#binaries
          format: table
          scan-ref: target/debug/fetter
          severity: HIGH,CRITICAL
          scanners: vuln
          ignore-unfixed: true
          exit-code: 1
          trivy-config: trivy.yaml

  #-----------------------------------------------------------------------------
  quality:
    name: Quality
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v6

      - name: Install Rust
        uses: actions-rust-lang/setup-rust-toolchain@v1
        with:
          components: "rustfmt, clippy"

      - name: Check formatting
        run: |
          cargo fmt -- --check

      - name: Lint with Clippy
        run: |
          cargo clippy -- -D warnings

  #-----------------------------------------------------------------------------
  security:
    name: Security Checks
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      # https://github.com/aquasecurity/trivy-action?tab=readme-ov-file#skipping-setup-when-calling-trivy-action-multiple-times
      # The first call to the action will invoke setup-trivy and install trivy
      - name: Generate Trivy vulnerability report
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: fs
          output: trivy-report.json
          format: json
          scan-ref: .
          exit-code: 0
          trivy-config: trivy.yaml

      - name: Upload vulnerability report
        uses: actions/upload-artifact@v6
        with:
          name: trivy-report
          path: trivy-report.json
          retention-days: 30

      - name: Fail build on high/critical vulnerabilities
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: fs
          format: table
          scan-ref: .
          severity: HIGH,CRITICAL
          ignore-unfixed: true
          exit-code: 1
          # On a subsequent call trivy is already installed so we can skip setup
          skip-setup-trivy: true
          trivy-config: trivy.yaml

  #-----------------------------------------------------------------------------
#   coverage:
#     name: Coverage
#     runs-on: ubuntu-latest

#     steps:
#       - uses: actions/checkout@v6

#       - name: Install Rust
#         run: |
#           rustup install --no-self-update nightly && rustup default nightly
#           rustup component add llvm-tools-preview

#       - name: Configure cache
#         uses: actions/cache@v3
#         with:
#           path: |
#             ~/.cargo/bin/
#             ~/.cargo/registry
#             target/
#           key: coverage-${{ hashFiles('**/Cargo.lock') }}

#       - name: Conditionally install grcov
#         run: |
#           if ! command -v grcov &> /dev/null
#           then
#             cargo install grcov
#           fi

#       - name: Build
#         env:
#           RUSTFLAGS: -Cinstrument-coverage
#         run: cargo build

#       - name: Test
#         env:
#           LLVM_PROFILE_FILE: grcov-%p-%m.profraw
#           RUSTFLAGS: -Cinstrument-coverage
#         run: cargo test

#       - name: Generate coverage
#         run: |
#           grcov . -s . --binary-path ./target/debug/ -t lcov --branch --ignore-not-existing --excl-line cov-excl-line -o coverage.lcov

#       - name: Upload coverage reports to Codecov
#         uses: codecov/codecov-action@v4.0.1
#         with:
#           token: 6ecdfb7b-306b-4bdc-abbf-c393da9186c9
#           files: coverage.lcov
#           slug: flexatone/xensieve-rs

  #-----------------------------------------------------------------------------
  publish:
    name: Publish
    if: github.event_name == 'release'

    needs: [build-and-test, quality]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - name: Install Rust
        uses: actions-rust-lang/setup-rust-toolchain@v1

      - name: Publish
        run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }}