name: CI

on:
  push:
    branches: [main]
    paths-ignore:
      - ".github/workflows/nightly.yml"
      - ".github/workflows/release.yml"
      - "**/*.md"
      - ".cursor/**"
      - ".windsurf/**"
      - ".gitignore"
      - "assets/**"
      - "iac/**"
      - "LICENSE"
      - ".github/renovate.json5"
      - ".dockerignore"
      - "Dockerfile"
      - "xtp-plugin-schema.json"
      - ".hadolint.yaml"
      - "pre-commit-config.yaml"
  pull_request:
    branches: [main]
  workflow_dispatch:

env:
  CARGO_TERM_COLOR: always
  CARGO_INCREMENTAL: 1

permissions:
  contents: read # checkout repository
  packages: read # pull container images for integration tests

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  cache-prep:
    name: Prepare Cache
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          fetch-depth: 0
          submodules: true
          persist-credentials: false

      - name: Install Rust toolchain
        run: rustup show

      - name: Cache cargo registry
        uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
        with:
          path: |
            ~/.cargo/registry/index/
            ~/.cargo/registry/cache/
            ~/.cargo/git/db/
          key: cargo-registry-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: |
            cargo-registry-

      - name: Cache target directory
        uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
        with:
          path: target/
          key: cargo-target-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}-${{ github.sha }}
          restore-keys: |
            cargo-target-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}-
            cargo-target-${{ runner.os }}-

      - name: Build all workspace artifacts and tests
        run: |
          cargo check --workspace --all-features --locked
          cargo test --no-run --workspace --all-features --locked

  clippy:
    name: Clippy
    runs-on: ubuntu-latest
    needs: cache-prep

    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          fetch-depth: 0
          submodules: true
          persist-credentials: false

      - name: Install Rust toolchain
        run: rustup show

      - name: Restore cargo registry
        uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
        with:
          path: |
            ~/.cargo/registry/index/
            ~/.cargo/registry/cache/
            ~/.cargo/git/db/
          key: cargo-registry-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: |
            cargo-registry-

      - name: Restore target directory
        uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
        with:
          path: target/
          key: cargo-target-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}-${{ github.sha }}
          restore-keys: |
            cargo-target-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}-
            cargo-target-${{ runner.os }}-

      - name: Restore file timestamps
        run: |
          # Update source file timestamps to prevent unnecessary rebuilds
          # This makes cargo think files haven't changed since the cache was created
          git ls-files -z | xargs -0 touch

      - name: Run clippy
        run: cargo clippy --workspace --all-features --locked -- -D warnings

  formatting:
    name: Check Formatting
    runs-on: ubuntu-latest
    needs: cache-prep

    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          fetch-depth: 0
          submodules: true
          persist-credentials: false

      - name: Install Rust toolchain
        run: rustup show

      - name: Check formatting
        run: cargo fmt -- --check

  tests:
    name: Run Tests
    runs-on: ubuntu-latest
    needs: cache-prep

    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          fetch-depth: 0
          submodules: true
          persist-credentials: false

      - name: Install Rust toolchain
        run: rustup show

      - name: Restore cargo registry
        uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
        with:
          path: |
            ~/.cargo/registry/index/
            ~/.cargo/registry/cache/
            ~/.cargo/git/db/
          key: cargo-registry-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: |
            cargo-registry-

      - name: Restore target directory
        uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
        with:
          path: target/
          key: cargo-target-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}-${{ github.sha }}
          restore-keys: |
            cargo-target-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}-
            cargo-target-${{ runner.os }}-

      - name: Restore file timestamps
        run: |
          # Update source file timestamps to prevent unnecessary rebuilds
          # This makes cargo think files haven't changed since the cache was created
          git ls-files -z | xargs -0 touch

      - name: Log in to GitHub Container Registry
        uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Install cosign
        uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1

      - name: Run tests
        env:
          GHCR_USERNAME: ${{ github.actor }}
          GHCR_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: RUST_BACKTRACE=1 cargo test --workspace --all-features --locked

  sbom:
    name: SBOM
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          fetch-depth: 0
          submodules: true
          persist-credentials: false

      - name: Install cargo-cyclonedx
        run: cargo install cargo-cyclonedx --locked

      - name: Generate CycloneDX SBOM
        run: cargo cyclonedx -f json --all-features --override-filename temp

      - name: Clean SBOM
        run: jq '
          del(.metadata.component."bom-ref")
          | del(.metadata.component.purl)
          | del(.metadata.component.components[0]."bom-ref")
          | del(.metadata.component.components[0].purl)
          ' temp.json > sbom.cdx.json

  # Summary job to check if all required jobs passed
  ci-success:
    name: CI Success
    runs-on: ubuntu-latest
    needs: [clippy, formatting, tests, sbom]

    steps:
      - name: All jobs succeeded
        run: echo "All CI jobs passed!"
