name: Code Style on: pull_request: jobs: format: name: Formatting runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v6 - name: Install Rust uses: dtolnay/rust-toolchain@stable with: components: rustfmt - name: Check formatting run: cargo fmt --all -- --check deny-check: name: cargo-deny runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v6 - name: Run cargo deny uses: EmbarkStudios/cargo-deny-action@v2 clippy: name: Clippy (${{ matrix.name }}) runs-on: ubuntu-latest strategy: fail-fast: false matrix: include: - name: all-features flags: "--all-features" - name: default flags: "" - name: libsql-only flags: "--no-default-features --features libsql" steps: - name: Checkout repository uses: actions/checkout@v6 - name: Install Rust uses: dtolnay/rust-toolchain@stable with: components: clippy - uses: Swatinem/rust-cache@v2 with: key: clippy-${{ matrix.name }} - name: Check lints run: cargo clippy --all --benches --tests --examples ${{ matrix.flags }} -- -D warnings clippy-windows: name: Clippy Windows (${{ matrix.name }}) if: github.base_ref == 'main' runs-on: windows-latest strategy: fail-fast: false matrix: include: - name: all-features flags: "--all-features" - name: default flags: "" - name: libsql-only flags: "--no-default-features --features libsql" steps: - name: Checkout repository uses: actions/checkout@v6 - name: Install Rust uses: dtolnay/rust-toolchain@stable with: components: clippy - uses: Swatinem/rust-cache@v2 with: key: clippy-windows-${{ matrix.name }} - name: Check lints run: cargo clippy --all --benches --tests --examples ${{ matrix.flags }} -- -D warnings no-panics: name: No panics in production code runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v6 with: fetch-depth: 0 - name: Check for .unwrap(), .expect(), assert!() in production code run: | BASE="${{ github.event.pull_request.base.sha }}" # Get the full diff for .rs files (production only, exclude tests/ directory) DIFF=$(git diff "$BASE"...HEAD -- 'src/**/*.rs' 'crates/**/*.rs' || true) if [ -z "$DIFF" ]; then echo "No production Rust changes detected." exit 0 fi # Extract added lines, skipping those inside test modules. # Track whether we're inside a test module by watching hunk headers # (lines starting with @@) whose context contains "mod tests" or "#[cfg(test)]". ADDED=$(echo "$DIFF" | awk ' /^@@/ { # Hunk context (after the second @@) tells us the function/module scope in_test = (tolower($0) ~ /mod tests/ || $0 ~ /#\[cfg\(test\)\]/ || $0 ~ /#\[test\]/) } /^\+[^+]/ && !in_test { print } ' || true) if [ -z "$ADDED" ]; then echo "No production Rust changes detected (test-only changes excluded)." exit 0 fi # Match panic-inducing patterns, excluding safety suppressions VIOLATIONS=$(echo "$ADDED" \ | grep -E '\.(unwrap|expect)\(|[^_]assert(_eq|_ne)?!' \ | grep -Ev 'debug_assert|// safety:' \ || true) if [ -n "$VIOLATIONS" ]; then echo "::error::Found .unwrap(), .expect(), or assert!() in production code." echo "Production code must use proper error handling instead of panicking." echo "Suppress false positives with an inline '// safety: ' comment." echo "" echo "$VIOLATIONS" | head -20 echo "" COUNT=$(echo "$VIOLATIONS" | wc -l | tr -d ' ') echo "Total: $COUNT violation(s)" exit 1 fi echo "OK: No panic-inducing calls in changed production code." # Roll-up job for branch protection code-style: name: Code Style (fmt + clippy + deny) runs-on: ubuntu-latest if: always() needs: [format, clippy, clippy-windows, deny-check, no-panics] steps: - run: | if [[ "${{ needs.format.result }}" != "success" || "${{ needs.clippy.result }}" != "success" || "${{ needs.deny-check.result }}" != "success" || "${{ needs.no-panics.result }}" != "success" ]]; then echo "One or more jobs failed" exit 1 fi # clippy-windows only runs on main PRs, so skipped is acceptable but failure is not if [[ "${{ needs.clippy-windows.result }}" != "success" && "${{ needs.clippy-windows.result }}" != "skipped" ]]; then echo "Windows clippy failed: ${{ needs.clippy-windows.result }}" exit 1 fi