# Whenever an open PR is updated, the workflow will be triggered
#
# This can get expensive, so we do a lot of caching and checks to prevent unnecessary runs

name: Rust CI

on:
  push:
    branches:
      - main
    paths:
      - packages/**
      - examples/**
      - docs/guide/**
      - src/**
      - .github/**
      - lib.rs
      - Cargo.toml
      - Makefile.toml

  pull_request:
    types: [opened, synchronize, reopened, ready_for_review]
    branches:
      - main
    paths:
      - packages/**
      - examples/**
      - src/**
      - .github/**
      - lib.rs
      - Cargo.toml

# workflow_dispatch:
concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
  cancel-in-progress: true

env:
  CARGO_TERM_COLOR: always
  CARGO_INCREMENTAL: 0 # todo(jon): cargo-cache wipes incremental artifacts, but we eventually want to cache them
  RUST_BACKTRACE: 1
  rust_nightly: nightly-2025-04-07

jobs:
  check-msrv:
    if: github.event.pull_request.draft == false
    name: Check MSRV
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@1.86.0
      - uses: Swatinem/rust-cache@v2
        with:
          cache-all-crates: "true"
      # https://github.com/foresterre/cargo-msrv/blob/4345edfe3f4fc91cc8ae6c7d6804c0748fae92ae/.github/workflows/msrv.yml
      - name: install_cargo_msrv
        run: cargo install cargo-msrv --all-features --version 0.16.3 --locked
      - name: version_of_cargo_msrv
        run: cargo msrv --version
      - name: run_cargo_msrv
        run: cargo msrv --output-format json verify -- cargo check
      - name: run_cargo_msrv_on_verify_failure
        if: ${{ failure() }}
        run: cargo msrv --output-format json -- cargo check

  test:
    if: github.event.pull_request.draft == false
    name: Test Suite
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v4
      - name: Free Disk Space
        uses: ./.github/actions/free-disk-space
      - uses: awalsh128/cache-apt-pkgs-action@latest
        with:
          packages: libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev
          version: 1.0
      - uses: dtolnay/rust-toolchain@1.86.0
        with:
          components: rustfmt, clippy
      - uses: Swatinem/rust-cache@v2
        with:
          cache-all-crates: "true"
      - uses: browser-actions/setup-firefox@latest
      - run: cargo test --lib --bins --tests --examples --workspace --exclude dioxus-desktop --exclude dioxus-mobile

  release-test:
    if: github.event.pull_request.draft == false
    name: Test Suite with Optimizations
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v4
      - name: Free Disk Space
        uses: ./.github/actions/free-disk-space
      - uses: awalsh128/cache-apt-pkgs-action@latest
        with:
          packages: libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev
          version: 1.0
      - uses: dtolnay/rust-toolchain@1.86.0
        with:
          components: rustfmt, clippy
      - uses: Swatinem/rust-cache@v2
        with:
          cache-all-crates: "true"
      - uses: browser-actions/setup-firefox@latest
      - run: cargo test --lib --bins --tests --examples --workspace --exclude dioxus-desktop --exclude dioxus-mobile --profile release-unoptimized

  fmt:
    if: github.event.pull_request.draft == false
    name: Rustfmt
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@1.86.0
        with:
          components: rustfmt
      - uses: Swatinem/rust-cache@v2
        with:
          cache-all-crates: "true"
      - run: cargo fmt --all -- --check

  docs:
    if: github.event.pull_request.draft == false
    name: Docs
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v4
      - uses: awalsh128/cache-apt-pkgs-action@latest
        with:
          packages: libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev
          version: 1.0
      - name: Install Rust ${{ env.rust_nightly }}
        uses: dtolnay/rust-toolchain@nightly
        with:
          toolchain: ${{ env.rust_nightly }}
      - uses: Swatinem/rust-cache@v2
        with:
          cache-all-crates: "true"
      - name: "doc --lib --all-features"
        run: |
          cargo doc --workspace --no-deps --all-features --document-private-items
        env:
          RUSTDOCFLAGS: -Dwarnings --document-private-items

  check:
    if: github.event.pull_request.draft == false
    name: Check
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v4
      - uses: awalsh128/cache-apt-pkgs-action@latest
        with:
          packages: libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev
          version: 1.0
      - uses: dtolnay/rust-toolchain@1.86.0
      - uses: Swatinem/rust-cache@v2
        with:
          cache-all-crates: "true"
      - run: cargo check --workspace --all-features --all-targets

  clippy:
    if: github.event.pull_request.draft == false
    name: Clippy
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v4
      - uses: awalsh128/cache-apt-pkgs-action@latest
        with:
          packages: libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev
          version: 1.0
      - uses: dtolnay/rust-toolchain@1.86.0
        with:
          components: rustfmt, clippy
      - uses: Swatinem/rust-cache@v2
        with:
          cache-all-crates: "true"
      - run: cargo clippy --workspace --examples --tests --all-features --all-targets -- -D warnings

  nix:
    if: github.event.pull_request.draft == false
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-24.04, macos-latest]
    steps:
      - uses: actions/checkout@v4
      - uses: nixbuild/nix-quick-install-action@master
      - uses: nix-community/cache-nix-action@main
        with:
          primary-key: nix-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/*.nix', '**/flake.lock') }}
          restore-prefixes-first-match: nix-${{ runner.os }}-${{ runner.arch }}
      - name: Install omnix
        run: nix --accept-flake-config profile install "github:juspay/omnix"
      - name: Build all flake outputs
        run: om ci
      - name: Ensure devShell has all build deps
        run: nix develop -c cargo build -p dioxus-cli --features no-downloads

  playwright:
    if: github.event.pull_request.draft == false
    name: Playwright Tests
    runs-on: macos-latest
    steps:
      # Do our best to cache the toolchain and node install steps
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 16
      - name: Install Rust
        uses: dtolnay/rust-toolchain@1.86.0
        with:
          toolchain: stable
          targets: x86_64-unknown-linux-gnu,wasm32-unknown-unknown
      - uses: Swatinem/rust-cache@v2
        with:
          cache-all-crates: "true"
          cache-on-failure: "true"
      - name: Playwright
        working-directory: ./packages/playwright-tests
        run: |
          npm ci
          npm install -D @playwright/test
          npx playwright install --with-deps
          npx playwright test
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: playwright-report
          path: ./packages/playwright-tests/playwright-report/
          retention-days: 30

  matrix_test:
    runs-on: ${{ matrix.platform.os }}
    if: github.event.pull_request.draft == false
    env:
      RUST_CARGO_COMMAND: ${{ matrix.platform.cross == true && 'cross' || 'cargo' }}
    strategy:
      matrix:
        platform:
          - {
              target: aarch64-apple-darwin,
              os: macos-latest,
              toolchain: "1.86.0",
              cross: false,
              command: "test",
              args: "--all --tests",
            }
          - {
              target: x86_64-apple-darwin,
              os: macos-13,
              toolchain: "1.86.0",
              cross: false,
              command: "test",
              args: "--all --tests",
            }
          - {
              target: aarch64-apple-ios,
              os: macos-latest,
              toolchain: "1.86.0",
              cross: false,
              command: "build",
              args: "--package dioxus-mobile",
            }
          - {
              target: x86_64-unknown-linux-gnu,
              os: ubuntu-24.04,
              toolchain: "1.86.0",
              cross: false,
              command: "build",
              args: "--all --tests",
            }
          - {
              target: aarch64-unknown-linux-gnu,
              os: ubuntu-24.04-arm,
              toolchain: "1.86.0",
              cross: false,
              command: "build",
              args: "--all --tests",
            }
          - {
              target: aarch64-linux-android,
              os: ubuntu-24.04,
              toolchain: "1.86.0",
              cross: true,
              command: "build",
              args: "--package dioxus-mobile",
            }

    steps:
      - uses: actions/checkout@v4
      - name: install stable
        uses: dtolnay/rust-toolchain@master
        with:
          toolchain: ${{ matrix.platform.toolchain }}
          targets: ${{ matrix.platform.target }}
          components: rustfmt

      - name: Install nasm for windows (tls)
        if: ${{ matrix.platform.target == 'x86_64-pc-windows-msvc' }}
        uses: ilammy/setup-nasm@v1

      - name: Free Disk Space
        if: ${{ matrix.platform.os == 'ubuntu-24.04' || matrix.platform.os == 'ubuntu-24.04-arm' }}
        uses: ./.github/actions/free-disk-space

      - uses: awalsh128/cache-apt-pkgs-action@latest
        if: ${{ matrix.platform.os == 'ubuntu-24.04' || matrix.platform.os == 'ubuntu-24.04-arm' }}
        with:
          packages: libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev
          version: 1.0

      - name: Install cross
        if: ${{ matrix.platform.cross == true }}
        uses: taiki-e/install-action@cross

      - uses: Swatinem/rust-cache@v2
        with:
          key: "matrix-${{ matrix.platform.target }}"
          cache-all-crates: "true"

      - name: test
        run: |
          ${{ env.RUST_CARGO_COMMAND }} ${{ matrix.platform.command }} ${{ matrix.platform.args }} --target ${{ matrix.platform.target }}

  # borrowed from uv
  # https://raw.githubusercontent.com/astral-sh/uv/refs/heads/main/.github/workflows/ci.yml
  cargo-test-windows:
    if: github.event.pull_request.draft == false
    runs-on:
      labels: "windows-latest"
    name: "cargo test | windows"
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@1.86.0
        with:
          components: rustfmt, clippy
      - uses: Swatinem/rust-cache@v2
        with:
          workspaces: ${{ env.UV_WORKSPACE }}
          cache-all-crates: "true"

      - name: "Install Rust toolchain"
        working-directory: ${{ env.UV_WORKSPACE }}
        run: rustup show

      - name: "Cargo test"
        working-directory: ${{ env.UV_WORKSPACE }}
        run: |
          cargo test --workspace --tests

  # Only run semver checks if the PR is not a draft and does not have the breaking label
  # Breaking PRs don't need to follow semver since they are breaking changes
  # However, this means we won't attempt to backport them, so you should be careful about using this label, as it will
  # likely make future backporting difficult
  #
  # todo: fix this so even if breaking changes have been merged, the fix can be backported
  #
  # This will stop working once the first breaking change has been merged, so we should really try to just backport the fix
  # and *then* run the semver checks. Basically "would backporting this PR cause a breaking change on stable?"
  #
  # semver:
  #   if: github.event.pull_request.draft == false && !contains(github.event.pull_request.labels.*.name, 'breaking')
  #   name: Semver Check
  #   runs-on: ubuntu-24.04
  #   steps:
  #     - uses: actions/checkout@v4
  #     - uses: dtolnay/rust-toolchain@1.86.0
  #     - uses: Swatinem/rust-cache@v2
  #       with:
  #         cache-all-crates: "true"
  #         cache-on-failure: "true"
  #     - name: Check semver
  #       uses: obi1kenobi/cargo-semver-checks-action@v2
  #       with:
  #         manifest-path: ./Cargo.toml
  #         exclude: "dioxus-cli, dioxus-ext"
