name: rustls-ffi

permissions:
  contents: read

on:
  push:
  pull_request:
  merge_group:
  schedule:
    - cron: '15 12 * * 3'

jobs:
  build:
    name: Build+test
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        # test a bunch of toolchain and crypto providers on ubuntu
        cc: [ clang, gcc ]
        crypto: [ aws-lc-rs, ring ]
        rust:
          - stable
          - beta
          - nightly
          # MSRV - keep in sync with what rustls and rustls-platform-verifier
          # consider MSRV
          - 1.64.0
        os: [ ubuntu-latest ]
        # but only stable, clang, and aws-lc-rs on macos (slower platform)
        include:
          - os: macos-latest
            cc: clang
            crypto: aws-lc-rs
            rust: stable
    steps:
      - name: Checkout sources
        uses: actions/checkout@v4
        with:
          persist-credentials: false

      - name: Install ${{ matrix.rust }} toolchain
        uses: dtolnay/rust-toolchain@master
        with:
          toolchain: ${{ matrix.rust }}
      - name: Unit tests
        env:
          CARGO_UNSTABLE_HTTP_REGISTRY: true
        run: make CC=${{ matrix.cc }} PROFILE=debug CRYPTO_PROVIDER=${{ matrix.crypto }} test
      - name: Platform verifier connect test
        run: make PROFILE=debug CRYPTO_PROVIDER=${{ matrix.crypto }} connect-test
      - name: Integration tests
        env:
          CARGO_UNSTABLE_HTTP_REGISTRY: true
        # Note: we run this after the connect-tests because the static libs test rebuilds the crate
        #       squashing whatever RUSTFLAGS the Makefile has set and producing a librustls_ffi.a
        #       for the default build config.
        run: make CC=${{ matrix.cc }} PROFILE=debug CRYPTO_PROVIDER=${{ matrix.crypto }} integration
      - name: Verify debug builds were using ASAN
        if: runner.os == 'Linux' # For 'nm'
        run: |
          nm target/client | grep '__asan_init'
          nm target/server | grep '__asan_init'
      - name: Build release binaries
        run: |
          make clean
          make CC=${{ matrix.cc }} CRYPTO_PROVIDER=${{ matrix.crypto }} PROFILE=release
      - name: Verify release builds were not using ASAN
        if: runner.os == 'Linux' # For 'nm'
        run: |
          nm target/client | grep -v '__asan_init'
          nm target/server | grep -v '__asan_init'

  valgrind:
    name: Valgrind
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          persist-credentials: false
      - name: Install valgrind
        run: sudo apt-get update && sudo apt-get install -y valgrind
      - run: VALGRIND=valgrind make PROFILE=release test integration

  test-windows-cmake-debug:
    name: Windows CMake, Debug configuration
    runs-on: windows-latest
    strategy:
      matrix:
        crypto: [ aws-lc-rs, ring ]
    steps:
      - uses: actions/checkout@v4
        with:
          persist-credentials: false
      - name: Install nightly rust toolchain
        uses: dtolnay/rust-toolchain@nightly
      - name: Install NASM for aws-lc-rs
        uses: ilammy/setup-nasm@v1
      - name: Configure CMake
        run: cmake -DCRYPTO_PROVIDER="${{ matrix.crypto }}" -S . -B build
      - name: Build, debug configuration
        run: cmake --build build --config Debug
      - name: Integration test, debug configuration
        run: cargo test --no-default-features --features="${{ matrix.crypto }}" --locked --test client_server client_server_integration -- --ignored --exact
        env:
          CLIENT_BINARY: D:\a\rustls-ffi\rustls-ffi\build\tests\Debug\client.exe
          SERVER_BINARY: D:\a\rustls-ffi\rustls-ffi\build\tests\Debug\server.exe

  test-windows-cmake-release:
    name: Windows CMake, Release configuration
    runs-on: windows-latest
    strategy:
      matrix:
        crypto: [ aws-lc-rs, ring ]
    steps:
      - uses: actions/checkout@v4
        with:
          persist-credentials: false
      - name: Install nightly rust toolchain
        uses: dtolnay/rust-toolchain@nightly
      - name: Install NASM for aws-lc-rs
        uses: ilammy/setup-nasm@v1
      - name: Configure CMake
        run: cmake -DCRYPTO_PROVIDER="${{ matrix.crypto }}" -S . -B build
      - name: Build, release configuration
        run: cmake --build build --config Release
      - name: Integration test, release configuration
        run: cargo test --no-default-features --features="${{ matrix.crypto }}" --locked --test client_server client_server_integration -- --ignored --exact
        env:
          CLIENT_BINARY: D:\a\rustls-ffi\rustls-ffi\build\tests\Release\client.exe
          SERVER_BINARY: D:\a\rustls-ffi\rustls-ffi\build\tests\Release\server.exe

  ensure-header-updated:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          persist-credentials: false
      - name: Install nightly rust toolchain
        uses: dtolnay/rust-toolchain@nightly
      - name: Install cbindgen
        # Pin the installed version of cbindgen so that local usage can be
        # reliably matched to CI. There can be non-semantic differences in
        # output between point releases of cbindgen that will fail this check
        # otherwise.
        run: cargo install cbindgen --force --version 0.27.0
      - run: touch src/lib.rs
      - run: cbindgen --version
      - run: make src/rustls.h
      - run: git diff --exit-code

  docs:
    name: Check for documentation errors
    runs-on: ubuntu-latest
    steps:
      - name: Checkout sources
        uses: actions/checkout@v4
        with:
          persist-credentials: false

      - name: Install rust toolchain
        uses: dtolnay/rust-toolchain@nightly

      - name: cargo doc (all features)
        run: cargo doc --all-features --no-deps --workspace
        env:
          RUSTDOCFLAGS: -Dwarnings

  minver:
    name: Check minimum versions
    runs-on: ubuntu-latest
    steps:
      - name: Checkout sources
        uses: actions/checkout@v4
        with:
          persist-credentials: false

      - name: Install rust toolchain
        uses: dtolnay/rust-toolchain@nightly

      - name: Build client/server binaries
        run: make target/client target/server

      - name: cargo test (debug; default features; -Z minimal-versions)
        run: cargo -Z minimal-versions test --locked

      - name: cargo test (debug; ring; -Z minimal-versions)
        run: cargo -Z minimal-versions test --no-default-features --features=ring --locked

  format:
    name: Format
    runs-on: ubuntu-latest
    steps:
      - name: Checkout sources
        uses: actions/checkout@v4
        with:
          persist-credentials: false
      - name: Install rust toolchain
        uses: dtolnay/rust-toolchain@master
        with:
          toolchain: 1.67.1
          components: rustfmt
      - name: Check Rust formatting
        run: cargo fmt --all -- --check
      - name: Check C formatting
        run: make format-check

  clippy:
    name: Clippy
    runs-on: ubuntu-latest
    steps:
      - name: Checkout sources
        uses: actions/checkout@v4
        with:
          persist-credentials: false
      - name: Install rust toolchain
        uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy
      - name: Check clippy (default features)
        # We allow unknown lints here because sometimes the nightly job
        # (below) will have a new lint that we want to suppress.
        # If we suppress (e.g. #![allow(clippy::arc_with_non_send_sync)]),
        # we would get an unknown-lint error from older clippy versions.
        run: cargo clippy --locked --workspace --all-targets -- -D warnings -A unknown-lints
      - name: Check clippy (ring)
        run: cargo clippy --locked --workspace --all-targets --no-default-features --features=ring -- -D warnings -A unknown-lints

  clippy-nightly-optional:
    name: Clippy nightly (optional)
    runs-on: ubuntu-latest
    steps:
      - name: Checkout sources
        uses: actions/checkout@v4
        with:
          persist-credentials: false
      - name: Install rust toolchain
        uses: dtolnay/rust-toolchain@nightly
        with:
          components: clippy
      - name: Check clippy (default features)
        run: cargo clippy --locked --workspace --all-targets -- -D warnings
      - name: Check clippy (ring)
        run: cargo clippy --locked --workspace --all-targets --no-default-features --features=ring -- -D warnings
      - name: Check clippy (all features)
        # We only test --all-features on nightly, because two of the features
        # (read_buf, core_io_borrowed_buf) require nightly.
        run: cargo clippy --locked --workspace --all-targets --all-features -- -D warnings

  clang-tidy:
    name: Clang Tidy
    runs-on: ubuntu-latest
    steps:
      - name: Checkout sources
        uses: actions/checkout@v4
        with:
          persist-credentials: false
      - name: Clang tidy
        run: clang-tidy tests/*.c -- -I src/

  miri:
    name: Miri
    runs-on: ubuntu-latest
    steps:
      - name: Checkout sources
        uses: actions/checkout@v4
        with:
          persist-credentials: false

      - name: Install nightly Rust
        uses: dtolnay/rust-toolchain@nightly
      - run: rustup override set "nightly-$(curl -s https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/miri)"
      - run: rustup component add miri
      - run: cargo miri test
