name: build

on:
  push:
    branches:
      - main
  pull_request:
    branches: ["**"]
  schedule:
    # At 23:25 on Thursday.
    - cron: "25 23 * * 4"
  workflow_dispatch:

permissions: {}

jobs:
  test:
    strategy:
      fail-fast: false
      matrix:
        runs-on: [ubuntu-22.04, windows-2022, macos-latest]
        toolchain:
          - "1.62"
          - stable
          - nightly
        versions:
          - ""
          - -Zminimal-versions
        exclude:
          # package `regex v1.11.1` cannot be built because it requires rustc
          # 1.65 or newer, while the currently active rustc version is 1.62.0
          - toolchain: "1.62"
            versions: ""
          # Older proc-macro2 crates fail to compile on the latest nightly
          - toolchain: nightly
            versions: -Zminimal-versions
    runs-on: ${{ matrix.runs-on }}
    steps:
      - name: Checkout
        uses: actions/checkout@v6
        with:
          persist-credentials: false
      - name: Install Rust
        run: |
          rustup toolchain install ${{ matrix.toolchain }} --profile minimal --no-self-update
          rustup default ${{ matrix.toolchain }}
      - name: Update lockfile
        run: cargo generate-lockfile ${{ matrix.versions }}
        env:
          RUSTC_BOOTSTRAP: 1
      - run: cargo test --all-targets
      - run: cargo run --example stress-test

  build-wasm:
    strategy:
      fail-fast: false
      matrix:
        versions:
          - ""
          - -Zminimal-versions
        toolchain:
          - stable
          - nightly
        include:
          # Without `-Zminimal-versions` a too recent bumpalo version is selected. Newer versions use `edition = "2021"`.
          - versions: -Zminimal-versions
          - toolchain: "1.62"
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v6
        with:
          persist-credentials: false
      - name: Install Rust
        run: |
          rustup toolchain install ${{ matrix.toolchain }} --profile minimal --no-self-update --target wasm32-unknown-unknown
          rustup default ${{ matrix.toolchain }}
      - name: Update lockfile
        run: cargo generate-lockfile ${{ matrix.versions }}
        env:
          RUSTC_BOOTSTRAP: 1
      - run: cargo build --lib

  test-wasm:
    strategy:
      fail-fast: false
      matrix:
        toolchain:
          - stable
          - nightly
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v6
        with:
          persist-credentials: false
      - name: Install Rust
        run: |
          rustup toolchain install ${{ matrix.toolchain }} --profile minimal --no-self-update --target wasm32-unknown-unknown
          rustup default ${{ matrix.toolchain }}
      - name: Install Node
        uses: actions/setup-node@v6
        with:
          node-version: "lts/*"
      - name: Install "wasm-pack"
        uses: taiki-e/install-action@v2
        with:
          tool: wasm-pack
      - run: wasm-pack test --node

  test-wasi:
    strategy:
      fail-fast: false
      matrix:
        toolchain:
          - stable
          - nightly
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
        with:
          persist-credentials: false
      - name: Install Rust
        run: |
          rustup toolchain install ${{ matrix.toolchain }} --profile minimal --no-self-update --target wasm32-wasip1
          rustup default ${{ matrix.toolchain }}
      - env:
          CARGO_BUILD_TARGET: wasm32-wasip1
        run: cargo build --release --examples
      - name: Install wasmtime
        uses: bytecodealliance/actions/wasmtime/setup@6aecabac1eb1dcf7ed94ba9471974415ee2ebef2 # v1
      - name: Run example using wasmtime
        run: wasmtime ./target/wasm32-wasip1/release/examples/get_timezone.wasm

  test-emscripten:
    strategy:
      fail-fast: false
      matrix:
        toolchain:
          - stable
          - nightly
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
        with:
          persist-credentials: false
      - name: Install Rust
        run: |
          rustup toolchain install ${{ matrix.toolchain }} --profile minimal --no-self-update --target wasm32-unknown-emscripten
          rustup default ${{ matrix.toolchain }}
      - uses: mymindstorm/setup-emsdk@6ab9eb1bda2574c4ddb79809fc9247783eaf9021 # v14
      - env:
          CARGO_TARGET_WASM32_UNKNOWN_EMSCRIPTEN_RUNNER: node
          CARGO_BUILD_TARGET: wasm32-unknown-emscripten
        run: cargo test

  build-cross:
    strategy:
      fail-fast: false
      matrix:
        target:
          - s390x-unknown-linux-gnu
          - x86_64-unknown-freebsd
          - x86_64-unknown-illumos
          # # NetBSD disabled waiting for release with fix from
          # # https://github.com/cross-rs/cross/issues/1345
          # - x86_64-unknown-netbsd
          # # Solaris disabled while https://github.com/cross-rs/cross/issues/1599 is open
          # - sparcv9-sun-solaris
        versions:
          - ""
          - -Zminimal-versions
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v6
        with:
          persist-credentials: false
      - name: Install Rust
        run: |
          rustup toolchain install stable --profile minimal --no-self-update --target ${{ matrix.target }}
      - name: Update lockfile
        run: cargo generate-lockfile ${{ matrix.versions }}
        env:
          RUSTC_BOOTSTRAP: 1

      - name: Install "cross"
        uses: taiki-e/install-action@v2
        with:
          tool: cross

      - run: cross build --target ${{ matrix.target }} --examples

  build-android:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v6
        with:
          persist-credentials: false
      - name: Install Rust
        run: rustup toolchain install stable --profile minimal --no-self-update
      - name: Add Rust components
        run: rustup component add clippy rust-src
      - name: Add Rust toolchain
        run: rustup target add aarch64-linux-android armv7-linux-androideabi
      - name: Setup Android NDK
        uses: nttld/setup-ndk@v1
        id: setup-ndk
        with:
          ndk-version: r25b
          local-cache: true

      - name: Install "cargo ndk"
        uses: taiki-e/install-action@v2
        with:
          tool: cargo-ndk

      - name: Check
        run: cargo ndk check --lib
        env:
          ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}

      - name: Clippy
        run: cargo ndk clippy --lib
        env:
          ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}

      - name: Build library
        run: cargo ndk build --lib
        env:
          ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}

  build-ios-cross:
    strategy:
      fail-fast: false
      matrix:
        toolchain:
          - "1.62"
          - stable
        target:
          - x86_64-apple-ios
        versions:
          - ""
          - -Zminimal-versions
        exclude:
          # package `regex v1.11.1` cannot be built because it requires rustc
          # 1.65 or newer, while the currently active rustc version is 1.62.0
          - toolchain: "1.62"
            versions: ""
    runs-on: macos-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v6
        with:
          persist-credentials: false
      - name: Install Rust
        run: |
          rustup toolchain install ${{ matrix.toolchain }} --profile minimal --no-self-update --target ${{ matrix.target }}
          rustup default ${{ matrix.toolchain }}
      - name: Update lockfile
        run: cargo generate-lockfile ${{ matrix.versions }}
        env:
          RUSTC_BOOTSTRAP: 1
      - name: Install "cross"
        run: curl --location --silent --show-error --fail https://github.com/cargo-bins/cargo-quickinstall/releases/download/cross-0.2.5/cross-0.2.5-x86_64-apple-darwin.tar.gz | tar -xzvvf - -C $HOME/.cargo/bin
      - run: cross build --target ${{ matrix.target }} --examples

  check:
    strategy:
      fail-fast: false
      matrix:
        toolchain:
          - "1.62"
          - stable
          - nightly
        versions:
          - ""
          - -Zminimal-versions
        exclude:
          # package `cc v1.2.17` cannot be built because it requires rustc 1.63
          # or newer, while the currently active rustc version is 1.62.0
          - toolchain: "1.62"
            versions: ""
    runs-on: ubuntu-latest
    env:
      RUSTFLAGS: -D warnings
      RUST_BACKTRACE: 1
    steps:
      - name: Checkout
        uses: actions/checkout@v6
        with:
          persist-credentials: false
      - name: Install Rust
        run: |
          rustup toolchain install ${{ matrix.toolchain }} --profile minimal --no-self-update --component clippy
          rustup default ${{ matrix.toolchain }}
      - name: Update lockfile
        run: cargo generate-lockfile ${{ matrix.versions }}
        env:
          RUSTC_BOOTSTRAP: 1
      - run: cargo check --all-targets --workspace
      - run: cargo clippy --all-targets --workspace -- -D warnings

  no-docker-image-check-only:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v6
        with:
          persist-credentials: false
      - name: Install Rust
        run: |
          rustup toolchain install nightly --profile minimal --no-self-update --component rust-src
          rustup default nightly
      - run: cargo check --target x86_64-unknown-haiku -Z build-std --examples

  doc:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v6
        with:
          persist-credentials: false
      - name: Install Rust
        run: |
          rustup toolchain install nightly --profile minimal --no-self-update
          rustup default nightly
      - run: RUSTDOCFLAGS="-D warnings" cargo doc --all-features

  audit:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v6
        with:
          persist-credentials: false
      - uses: EmbarkStudios/cargo-deny-action@v2

  fallback:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v6
        with:
          persist-credentials: false
      - name: Install Rust
        run: |
          rustup toolchain install stable --profile minimal --no-self-update --target x86_64-fortanix-unknown-sgx
      # Should fail (outcome is negated):
      - run: if cargo build --lib --target x86_64-fortanix-unknown-sgx; then exit 1; fi
      # Should succeed:
      - run: cargo build --lib --target x86_64-fortanix-unknown-sgx --features fallback

  c:
    name: Lint and format C
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6
        with:
          persist-credentials: false

      - name: Setup Node.js runtime
        uses: actions/setup-node@v6
        with:
          node-version: "lts/*"
      - name: Install npm
        run: npm i -f -g npm@8.16.0

      - name: Lint and check formatting with clang-format
        run: npx github:artichoke/clang-format --check

  test-haiku:
    strategy:
      fail-fast: false
      matrix:
        runs-on: [ubuntu-22.04, windows-2022, macos-latest]
        toolchain:
          - stable
        versions:
          - ""
    runs-on: ${{ matrix.runs-on }}
    env:
      RUSTFLAGS: -D warnings
      RUST_BACKTRACE: 1
    steps:
      - name: Checkout
        uses: actions/checkout@v6
        with:
          persist-credentials: false
      - name: Install Rust
        run: |
          rustup toolchain install ${{ matrix.toolchain }} --profile minimal --no-self-update --target x86_64-fortanix-unknown-sgx
          rustup default ${{ matrix.toolchain }}
      - name: Update lockfile
        run: cargo generate-lockfile ${{ matrix.versions }}
        working-directory: haiku
        env:
          RUSTC_BOOTSTRAP: 1
      - run: cargo test --all-targets
        working-directory: haiku

  check-haiku:
    strategy:
      fail-fast: false
      matrix:
        toolchain:
          - stable
        versions:
          - ""
    runs-on: ubuntu-latest
    env:
      RUSTFLAGS: -D warnings
      RUST_BACKTRACE: 1
    steps:
      - name: Checkout
        uses: actions/checkout@v6
        with:
          persist-credentials: false
      - name: Install Rust
        run: |
          rustup toolchain install ${{ matrix.toolchain }} --profile minimal --no-self-update --component clippy
          rustup default ${{ matrix.toolchain }}
      - name: Update lockfile
        run: cargo generate-lockfile ${{ matrix.versions }}
        working-directory: haiku
        env:
          RUSTC_BOOTSTRAP: 1
      - run: cargo check --all-targets
        working-directory: haiku
      - run: cargo clippy --all-targets -- -D warnings
        working-directory: haiku

  check-all-versions:
    runs-on: ubuntu-22.04
    steps:
      - name: Checkout
        uses: actions/checkout@v6
        with:
          persist-credentials: false

      - name: Install Rust
        run: |
          rustup toolchain install nightly --profile minimal --no-self-update
          rustup default nightly

      - name: Install "cargo hack"
        uses: taiki-e/install-action@v2
        with:
          tool: cargo-hack

      - run: cargo hack check --version-range .. --version-step 2 --clean-per-version --clean-per-run

  generate-windows-bindings:
    name: Generate windows-core bindings with windows-bindgen
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
        with:
          persist-credentials: false
      - name: Clean
        run: rm -rf src/Windows.rs
      - name: Generate
        run: cargo r --manifest-path=api_gen/Cargo.toml
      - name: Diff generated Rust code
        run: test -z "$(git status --porcelain)" || (echo "::error::Generated files are different, please regenerate with cargo run --manifest-path=api_gen/Cargo.toml!"; git status; false)

  typos:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
        with:
          persist-credentials: false
      - uses: crate-ci/typos@master

  devskim:
    runs-on: ubuntu-latest
    permissions:
      actions: read
      contents: read
      security-events: write
    steps:
      - uses: actions/checkout@v6
        with:
          persist-credentials: false
      - name: Run DevSkim scanner
        uses: microsoft/DevSkim-Action@v1
      - name: Upload DevSkim scan results to GitHub Security tab
        uses: github/codeql-action/upload-sarif@v4
        with:
          sarif_file: devskim-results.sarif
