csvlint 1.0.0

A CSV linter that validates CSV files according to RFC 4180
Documentation
name: CI/CD

on:
  push:
    branches: [ main, master ]
  pull_request:
    branches: [ main, master ]
  release:
    types: [ published ]

env:
  CARGO_TERM_COLOR: always

jobs:
  test:
    name: Test
    runs-on: ubuntu-latest
    strategy:
      matrix:
        rust:
          - stable
          - beta
          - nightly
    continue-on-error: ${{ matrix.rust == 'nightly' }}

    steps:
    - uses: actions/checkout@v4

    - name: Install Rust toolchain
      uses: dtolnay/rust-toolchain@stable
      with:
        toolchain: ${{ matrix.rust }}
        components: rustfmt, clippy

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

    - name: Check formatting
      run: cargo fmt -- --check
      if: matrix.rust == 'stable'

    - name: Run clippy
      run: cargo clippy --all-targets --all-features -- -D warnings
      if: matrix.rust == 'stable'

    - name: Build
      run: cargo build --verbose

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

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

    - name: Test documentation
      run: cargo doc --no-deps --document-private-items
      if: matrix.rust == 'stable'

  security-audit:
    name: Security Audit
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: rustsec/audit-check@v2.0.0
      with:
        token: ${{ secrets.GITHUB_TOKEN }}

  coverage:
    name: Code Coverage
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: dtolnay/rust-toolchain@stable
      with:
        components: llvm-tools-preview

    - name: Install cargo-llvm-cov
      uses: taiki-e/install-action@cargo-llvm-cov

    - name: Generate code coverage
      run: cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info

    - name: Upload to codecov.io
      uses: codecov/codecov-action@v4
      with:
        token: ${{ secrets.CODECOV_TOKEN }}
        files: lcov.info
        fail_ci_if_error: false

  build:
    name: Build Release Binaries
    runs-on: ${{ matrix.os }}
    if: github.event_name == 'release'
    needs: [test, security-audit]
    strategy:
      matrix:
        include:
          - os: ubuntu-latest
            target: x86_64-unknown-linux-gnu
            binary_name: csvlint
          - os: ubuntu-latest
            target: x86_64-unknown-linux-musl
            binary_name: csvlint
          - os: windows-latest
            target: x86_64-pc-windows-msvc
            binary_name: csvlint.exe
          - os: macos-latest
            target: x86_64-apple-darwin
            binary_name: csvlint
          - os: macos-latest
            target: aarch64-apple-darwin
            binary_name: csvlint

    steps:
    - uses: actions/checkout@v4

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

    - name: Install musl tools (Linux musl only)
      if: matrix.target == 'x86_64-unknown-linux-musl'
      run: |
        sudo apt update
        sudo apt install -y musl-tools

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

    - name: Build release binary
      run: cargo build --release --target ${{ matrix.target }}

    - name: Strip binary (Unix only)
      if: matrix.os != 'windows-latest'
      run: strip target/${{ matrix.target }}/release/${{ matrix.binary_name }}

    - name: Create archive
      shell: bash
      run: |
        if [[ "${{ matrix.os }}" == "windows-latest" ]]; then
          archive_name="csvlint-${{ github.ref_name }}-${{ matrix.target }}.zip"
          cd target/${{ matrix.target }}/release
          7z a ../../../${archive_name} ${{ matrix.binary_name }}
        else
          archive_name="csvlint-${{ github.ref_name }}-${{ matrix.target }}.tar.gz"
          cd target/${{ matrix.target }}/release
          tar czf ../../../${archive_name} ${{ matrix.binary_name }}
        fi
        echo "ARCHIVE_NAME=${archive_name}" >> $GITHUB_ENV

    - name: Upload release artifact
      uses: actions/upload-artifact@v4
      with:
        name: ${{ env.ARCHIVE_NAME }}
        path: ${{ env.ARCHIVE_NAME }}

  release:
    name: Create Release
    runs-on: ubuntu-latest
    if: github.event_name == 'release'
    needs: [build]

    steps:
    - uses: actions/checkout@v4

    - name: Download all artifacts
      uses: actions/download-artifact@v4
      with:
        path: artifacts

    - name: Upload release assets
      shell: bash
      run: |
        set -euo pipefail
        echo "Uploading release assets..."
        for archive in artifacts/*/csvlint-*; do
          echo "Uploading ${archive}..."
          gh release upload "${{ github.ref_name }}" "${archive}" --clobber
        done
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  publish-crate:
    name: Publish to crates.io
    runs-on: ubuntu-latest
    if: github.event_name == 'release'
    needs: [test, security-audit]

    steps:
    - uses: actions/checkout@v4

    - name: Install Rust toolchain
      uses: dtolnay/rust-toolchain@stable

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

    - name: Publish to crates.io
      run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }}
      continue-on-error: true  # Allow failure if version already exists