# Enforce Tier 1B support tier.

# If you change the supported platforms in this workflow, please also
# update docs/support-tiers.md to match the changes you're making.

# Gating (see docs/support-tiers.md and docs/release-process.md): Tier 1B runs
# automatically on any PR whose base is a release-line (`stable`, `v0.*`) or
# release-candidate (`*-rc*`) branch -- this includes backport PRs and the
# release-plz release PR. For a PR against `main`, add the "check-release" label
# to run this suite on demand.
#
# On top of that scope, the suite only runs for trusted PRs (those whose branch
# lives in this repository, plus fork PRs a maintainer has labeled `safe to
# test`/`check-release`); see tier-1a.yml for why trust keys off the PR's head
# repository rather than `author_association`. Both conditions are combined once
# in the `authorize` job below into a single `run-suite` decision.

name: Tier 1B

on:
  # Daily full-suite run against main (nightly). Scheduled runs use the default
  # branch (main).
  schedule:
    - cron: '0 8 * * *'
  pull_request:
    types:
      - opened
      - reopened
      - synchronize
      - labeled
  # Run the full suite continuously on release-candidate branches during a
  # train's bake period, so breaking-change releases are validated end-to-end
  # before promotion. (On these push events there is no PR label, so the
  # per-job `if:` conditions below also accept `github.event_name == 'push'`.)
  push:
    branches:
      - '*-rc*'

jobs:
  authorize:
    name: Authorize CI run
    runs-on: ubuntu-latest
    outputs:
      run-suite: ${{ steps.decide.outputs.run-suite }}
    steps:
      - name: Determine whether to run the Tier 1B suite
        id: decide
        run: |
          echo "run-suite=${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository || contains(github.event.pull_request.labels.*.name, 'safe to test') || contains(github.event.pull_request.labels.*.name, 'check-release')) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event.pull_request.base.ref == 'stable' || startsWith(github.event.pull_request.base.ref, 'v0.') || contains(github.event.pull_request.base.ref, '-rc') || contains(github.event.pull_request.labels.*.name, 'release') || contains(github.event.pull_request.labels.*.name, 'check-release')) }}" >> "$GITHUB_OUTPUT"

  get-features:
    name: Get features
    needs: authorize
    runs-on: ubuntu-latest
    if: needs.authorize.outputs.run-suite == 'true'
    outputs:
      features: ${{ steps.get-features.outputs.features }}
      rust-native-features: ${{ steps.get-features.outputs.rust-native-features }}
      openssl-features: ${{ steps.get-features.outputs.openssl-features }}
    steps:
      - name: Checkout repository
        uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@fa04a1451ff1842e2626ccb99004d0195b455a88 # master@2026-06-30
        with:
          toolchain: stable

      - name: Get all features
        id: get-features
        run: |
          # Experimental (`unstable_`) features are deliberately excluded from
          # the required suite: they sit outside the SDK's normal support
          # commitments and must not gate main or the release branches. They are
          # exercised separately by the non-blocking experimental-features.yml.
          # See docs/experimental-features.md.
          FEATURES=$(cargo metadata --format-version=1 | jq -r '[.packages[] | select(.name=="c2pa") | .features | keys | map(select(. != "default" and (startswith("unstable_") | not))) | .[]] | unique | join(" ")')

          RUST_NATIVE_FEATURES=$(echo $FEATURES | sed 's/openssl//g')
          OPENSSL_FEATURES=$(echo $FEATURES | sed 's/rust_native_crypto//g')
          echo "features=$FEATURES" >> "$GITHUB_OUTPUT"
          echo "rust-native-features=$RUST_NATIVE_FEATURES" >> "$GITHUB_OUTPUT"
          echo "openssl-features=$OPENSSL_FEATURES" >> "$GITHUB_OUTPUT"

  tests-openssl:
    name: Unit tests (with OpenSSL installed)
    needs: [authorize, get-features]
    if: needs.authorize.outputs.run-suite == 'true'
    runs-on: ${{ matrix.os }}

    strategy:
      fail-fast: false
      matrix:
        os: [windows-latest, macos-latest, ubuntu-latest]
        rust_version: [stable, 1.88.0]

    steps:
      - name: Checkout repository
        uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@fa04a1451ff1842e2626ccb99004d0195b455a88 # master@2026-06-30
        with:
          toolchain: ${{ matrix.rust_version }}

      - name: Cache Rust dependencies
        uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1

      - name: Run tests
        env:
          RUST_BACKTRACE: "1"
          FEATURES: ${{needs.get-features.outputs.openssl-features}}
        run: |
          cargo test --features "$FEATURES"

  tests-rust-native-crypto:
    name: Unit tests (with Rust native crypto installed)
    needs: [authorize, get-features]
    if: needs.authorize.outputs.run-suite == 'true'
    runs-on: ${{ matrix.os }}

    strategy:
      fail-fast: false
      matrix:
        os: [windows-latest, macos-latest, ubuntu-latest]
        rust_version: [stable, 1.88.0]

    steps:
      - name: Checkout repository
        uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@fa04a1451ff1842e2626ccb99004d0195b455a88 # master@2026-06-30
        with:
          toolchain: ${{ matrix.rust_version }}

      - name: Cache Rust dependencies
        uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1

      - name: Run tests
        env:
          RUST_BACKTRACE: "1"
          FEATURES: ${{needs.get-features.outputs.rust-native-features}}
        run: |
          cargo test --features "$FEATURES"

  tests-cross:
    name: Unit tests
    needs: [authorize, get-features]
    if: needs.authorize.outputs.run-suite == 'true'
    runs-on: ubuntu-latest

    strategy:
      fail-fast: false
      matrix:
        target: [aarch64-unknown-linux-gnu]
        rust_version: [stable, 1.88.0]

    steps:
      - name: Checkout repository
        uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@fa04a1451ff1842e2626ccb99004d0195b455a88 # master@2026-06-30
        with:
          toolchain: ${{ matrix.rust_version }}
          targets: ${{ matrix.target }}

      - name: Install cross-compilation toolset
        run: cargo install cross

      - name: Cache Rust dependencies
        uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1

      # Integration tests are excluded because they require executing
      # the compiled binary, which isn't reliably possible when cross-compiling,
      # due to architecture mismatch.
      - name: Run unit tests (cross build)
        env:
          FEATURES: ${{needs.get-features.outputs.openssl-features}}
        run: |
          cross test --lib --bins --features "$FEATURES" --target ${{ matrix.target }}

  test-direct-minimal-versions:
    name: Unit tests with minimum versions of direct dependencies
    needs: [authorize, get-features]
    if: needs.authorize.outputs.run-suite == 'true'
    runs-on: ${{ matrix.os }}

    strategy:
      fail-fast: false
      matrix:
        os: [windows-latest, macos-latest, ubuntu-latest]

    steps:
      - name: Checkout repository
        uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@fa04a1451ff1842e2626ccb99004d0195b455a88 # master@2026-06-30
        with:
          toolchain: nightly-2025-12-06

      - name: Cache Rust dependencies
        uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1

      - name: Run tests
        env:
          FEATURES: ${{needs.get-features.outputs.openssl-features}}
        run: |
          cargo +nightly-2025-12-06 test -Z direct-minimal-versions --all-targets --features "$FEATURES"

  unused_deps:
    name: Check for unused dependencies
    needs: [authorize, get-features]
    if: needs.authorize.outputs.run-suite == 'true'
    runs-on: ubuntu-latest

    permissions:
      contents: write

    steps:
      - name: Checkout repository
        uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@fa04a1451ff1842e2626ccb99004d0195b455a88 # master@2026-06-30
        with:
          toolchain: nightly-2025-12-06
          # Pinning to specific nightly build for now. More recent versions
          # introduce a lifetime check that creates a whole slew of build
          # errors.

      - name: Install cargo-binstall
        uses: cargo-bins/cargo-binstall@732870f031d2fb36309d0deaf36abcc704a7be65 # v1.20.1

      - name: Install cargo-udeps
        run: cargo binstall --no-confirm cargo-udeps

      # Experimental (`unstable_`) features are excluded (see get-features); an
      # unstable-only optional dependency stays unactivated and so is never
      # miscounted as unused here.
      - name: Run cargo-udeps
        run: cargo udeps --all-targets --features "${{ needs.get-features.outputs.features }}"
