name: CI

on:
  push:
    branches:
      - master
  pull_request:
    branches:
      - master
  schedule:
    - cron: "00 01 * * *"
env:
  RUST_BACKTRACE: short
  # CI builds don't benefit very much from this.
  CARGO_INCREMENTAL: 0
  # We can't use a debugger in CI, and this makes builds faster and the cache
  # smaller. (TODO: use -Cdebuginfo=0 if it doesn't make backtraces useless)
  RUSTFLAGS: -Cdebuginfo=1

permissions:
  contents: read

jobs:
  test:
    name: Test ${{ matrix.target }}

    strategy:
      fail-fast: false

      matrix:
        include:
          - { target: x86_64-pc-windows-msvc, os: windows-latest }
          - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest }
          - { target: x86_64-apple-darwin, os: macos-latest }
          - target: x86_64-pc-windows-gnu
            os: windows-latest
            host: -x86_64-pc-windows-gnu

    runs-on: ${{ matrix.os }}

    steps:
      - uses: actions/checkout@v3
      # This has a matcher for test panics, so we use it even though elsewhere
      # we use actions-rs/toolchain.
      - uses: hecrj/setup-rust-action@v1
        with:
          rust-version: stable${{ matrix.host }}
          targets: ${{ matrix.target }}
      # The `{ sharedKey: ... }` allows different actions to share the cache.
      # We're using a `fullBuild` key mostly as a "this needs to do the
      # complete" that needs to do the complete build (that is, including
      # `--features 'bundled-full session buildtime_bindgen'`), which is very
      # slow, and has several deps.
      - uses: Swatinem/rust-cache@v2
        with: { sharedKey: fullBuild }

      - run: cargo build --features bundled --workspace --all-targets --verbose
      - run: cargo test --features bundled --workspace --all-targets --verbose
      - run: cargo test --features bundled --workspace --doc --verbose

      - name: Add llvm path on Windows
        if: matrix.os == 'windows-latest'
        run: echo "C:\msys64\mingw64\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append

      - run: cargo test --features 'bundled-full session buildtime_bindgen preupdate_hook' --all-targets --workspace --verbose
      - run: cargo test --features 'bundled-full session buildtime_bindgen preupdate_hook' --doc --workspace --verbose

      - name: loadable extension
        run: |
          cargo build --example loadable_extension --features "loadable_extension functions trace"
          cargo run --example load_extension --features "load_extension bundled functions trace"

      - name: macros
        run: |
          cargo test --package rusqlite-macros
          cargo test --features 'bundled rusqlite-macros'

      # TODO: move into own action for better caching
      - name: Static build
        # Do we expect this to work / should we test with gnu toolchain?
        if: matrix.os == 'x86_64-pc-windows-msvc'
        env:
          RUSTFLAGS: -Ctarget-feature=+crt-static -Cdebuginfo=1
        run: cargo build --features bundled

  test-sqlcipher-bundled:
    name: Test ${{ matrix.os }} (bundled SQLcipher + OpenSSL)
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, macos-latest]
        # TODO: find a way to test this on windows :(

    steps:
      - uses: actions/checkout@v3
      # This has a matcher for test panics, so we use it even though elsewhere
      # we use actions-rs/toolchain.
      - uses: hecrj/setup-rust-action@v1
        with:
          rust-version: stable${{ matrix.host }}
          targets: ${{ matrix.target }}
      - uses: Swatinem/rust-cache@v2
        with: { sharedKey: fullBuild }

      - run: cargo test --features 'bundled-sqlcipher' --workspace --all-targets --verbose
      - run: cargo test --features 'bundled-sqlcipher' --workspace --doc --verbose

      - run: cargo test --features 'modern-full bundled-sqlcipher' --workspace --all-targets --verbose
      - run: cargo test --features 'modern-full bundled-sqlcipher' --workspace --doc --verbose

      - run: cargo test --features 'bundled-sqlcipher-vendored-openssl' --workspace --all-targets --verbose
      - run: cargo test --features 'bundled-sqlcipher-vendored-openssl' --workspace --doc --verbose

      - run: cargo test --features 'modern-full bundled-sqlcipher-vendored-openssl' --all-targets --workspace --verbose
      - run: cargo test --features 'modern-full bundled-sqlcipher-vendored-openssl' --doc --workspace --verbose

      - name: Add llvm path on Windows
        if: matrix.os == 'windows-latest'
        run: echo "C:\msys64\mingw64\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append

      - run: cargo test --features 'bundled-full session buildtime_bindgen preupdate_hook' --all-targets --workspace --verbose
      - run: cargo test --features 'bundled-full session buildtime_bindgen preupdate_hook' --doc --workspace --verbose

  sqlcipher:
    name: Test with sqlcipher
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: hecrj/setup-rust-action@v1
      - uses: Swatinem/rust-cache@v2
      - run: sudo apt-get install sqlcipher libsqlcipher-dev
      - run: sqlcipher --version
      # TODO: Is it worth testing other features?
      - run: cargo build --features sqlcipher --workspace --all-targets --verbose
      - run: cargo test --features sqlcipher --workspace --all-targets --verbose

  sanitizer:
    name: Address Sanitizer
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      # Need nightly rust.
      - uses: hecrj/setup-rust-action@v1
        with:
          rust-version: nightly
          components: rust-src
      - uses: Swatinem/rust-cache@v2
      - name: Tests with asan
        env:
          RUSTFLAGS: -Zsanitizer=address -Cdebuginfo=0
          RUSTDOCFLAGS: -Zsanitizer=address
          ASAN_OPTIONS: "detect_stack_use_after_return=1:detect_leaks=0"
          # Work around https://github.com/rust-lang/rust/issues/59125 by
          # disabling backtraces. In an ideal world we'd probably suppress the
          # leak sanitization, but we don't care about backtraces here, so long
          # as the other tests have them.
          RUST_BACKTRACE: "0"
        run: cargo -Z build-std test --features 'bundled-full session buildtime_bindgen preupdate_hook with-asan' --target x86_64-unknown-linux-gnu

  # Ensure clippy doesn't complain.
  clippy:
    name: Clippy
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: hecrj/setup-rust-action@v1
        with:
          components: clippy
      - uses: Swatinem/rust-cache@v2
      - run: cargo clippy --all-targets --workspace --features bundled -- -D warnings
      # Clippy with all non-conflicting features
      - run: cargo clippy --all-targets --workspace --features 'bundled-full session buildtime_bindgen preupdate_hook' -- -D warnings

  # Ensure patch is formatted.
  fmt:
    name: Format
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: hecrj/setup-rust-action@v1
        with:
          components: rustfmt
      - run: cargo fmt --all -- --check

  # Detect cases where documentation links don't resolve and such.
  doc:
    name: Docs
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: hecrj/setup-rust-action@v1
      - uses: Swatinem/rust-cache@v2
        with: { sharedKey: fullBuild }
      - run: cargo doc --features 'bundled-full session buildtime_bindgen preupdate_hook' --no-deps
        env: { RUSTDOCFLAGS: -Dwarnings }

  codecov:
    name: Generate code coverage
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: 'llvm-tools-preview'
      - uses: taiki-e/install-action@main
        with:
          tool: grcov
      - name: Run tests for coverage
        run: |
          cargo test --verbose
          cargo test --features="bundled-full" --verbose
          cargo test --features="bundled-full session buildtime_bindgen preupdate_hook" --verbose
          cargo test --features="bundled-sqlcipher-vendored-openssl" --verbose
        env:
          RUSTFLAGS: -Cinstrument-coverage
          RUSTDOCFLAGS: -Cinstrument-coverage
          LLVM_PROFILE_FILE: rusqlite-%p-%m.profraw
      - name: Produce coverage info
        run: |
          grcov $(find . -name "rusqlite-*.profraw" -print) \
            -s . \
            --branch \
            --ignore-not-existing \
            --ignore='target/*' \
            --ignore='benches/*' \
            --ignore='/*' \
            --binary-path ./target/debug/ \
            --excl-line='#\[derive' \
            -t lcov \
            -o lcov.info
      - name: Upload to codecov.io
        uses: codecov/codecov-action@v3
        with:
          files: lcov.info
          fail_ci_if_error: true