name: CI

on:
  push:
    branches: [main]
  pull_request:

jobs:
  build-and-test:
    runs-on: ubuntu-latest
    steps:
      # fluxvm-image depends on the sibling `guestkit` project via a
      # relative path (../../../guestkit from crates/fluxvm-image), so it
      # has to land as an actual sibling directory here too, not nested
      # inside this checkout.
      - name: Checkout fluxvm
        uses: actions/checkout@v7
        with:
          path: fluxvm

      - name: Checkout guestkit (sibling path dependency)
        uses: actions/checkout@v7
        with:
          repository: zyvorai/guestkit
          path: guestkit

      - name: Install Rust
        uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy

      # guestkit's default feature set pulls in libsystemd-sys (journal-native
      # logging), which needs libsystemd-dev's pkg-config file — not present
      # on the runner image by default. guestkit's own CI hit and fixed this
      # exact gap; we need it too since we build guestkit as a dependency.
      - name: Install system dependencies
        # libhivex-dev: guestkit registry-write (Windows offline hive edits / agent-inject)
        # clang/llvm/libbpf-dev: fluxvm-container-agent build.rs compiles guest eBPF
        run: sudo apt-get update -qq && sudo apt-get install -y -qq libsystemd-dev libhivex-dev clang llvm libbpf-dev linux-libc-dev

      - name: Cache cargo
        uses: Swatinem/rust-cache@v2
        with:
          workspaces: fluxvm

      - name: cargo build --workspace
        working-directory: fluxvm
        run: cargo build --workspace --all-targets

      - name: cargo test --workspace
        working-directory: fluxvm
        run: cargo test --workspace

      - name: In-tree virtio-vsock CSM unit tests
        working-directory: fluxvm
        run: cargo test -p fluxvm-hypervisor --lib devices::virtio_vsock -- --nocapture

      - name: MicroVM gates (policy + unit + print-crd)
        working-directory: fluxvm
        run: |
          chmod +x scripts/test-microvm.sh scripts/test-microvm-policy.py
          ./scripts/test-microvm.sh

      - name: cargo clippy (informational — not a merge gate yet)
        working-directory: fluxvm
        run: cargo clippy --workspace --all-targets || true

  # Real-hardware regression test for `fluxctl build-image`'s guestkit-based
  # customization path (see scripts/test-image-customize.sh) across every
  # package-manager branch install_packages() supports. Doesn't need KVM —
  # build-image never boots a VM, it mounts the image directly via
  # guestkit's qemu-nbd + chroot — so this runs on stock GitHub-hosted
  # runners with no nested-virtualization dependency.
  image-customize:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        include:
          - distro: ubuntu
            image_url: https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img
            test_service: cron
          - distro: rocky
            image_url: https://dl.rockylinux.org/pub/rocky/9/images/x86_64/Rocky-9-GenericCloud.latest.x86_64.qcow2
            test_service: crond
          - distro: arch
            image_url: https://geo.mirror.pkgbuild.com/images/latest/Arch-Linux-x86_64-cloudimg.qcow2
            test_service: sshd
    steps:
      - name: Checkout fluxvm
        uses: actions/checkout@v7
        with:
          path: fluxvm

      - name: Checkout guestkit (sibling path dependency)
        uses: actions/checkout@v7
        with:
          repository: zyvorai/guestkit
          path: guestkit

      - name: Install Rust
        uses: dtolnay/rust-toolchain@stable

      - name: Install system dependencies
        run: sudo apt-get update -qq && sudo apt-get install -y -qq libsystemd-dev libhivex-dev qemu-utils parted

      - name: Cache cargo
        uses: Swatinem/rust-cache@v2
        with:
          workspaces: fluxvm
          key: image-customize-${{ matrix.distro }}

      - name: Build fluxctl + fluxvm-image (release)
        working-directory: fluxvm
        run: cargo build --release -p fluxctl -p fluxvm-image

      # Base cloud images are 500MB+ and rarely change — cache by URL so
      # only the first run per distro per cache-eviction cycle pays for it.
      - name: Cache base image
        id: cache-image
        uses: actions/cache@v6
        with:
          path: /tmp/base-image.qcow2
          key: image-customize-base-${{ matrix.distro }}-${{ matrix.image_url }}

      - name: Download base image
        if: steps.cache-image.outputs.cache-hit != 'true'
        run: curl -sSL --retry 3 --retry-delay 5 -o /tmp/base-image.qcow2 "${{ matrix.image_url }}"

      - name: modprobe nbd
        run: sudo modprobe nbd max_part=16

      - name: Run test-image-customize.sh
        working-directory: fluxvm
        env:
          FLUXVM_BIN: ${{ github.workspace }}/fluxvm/target/release/fluxctl
          TEST_PACKAGE: tree
          TEST_SERVICE: ${{ matrix.test_service }}
        run: sudo -E ./scripts/test-image-customize.sh --image /tmp/base-image.qcow2

      # #107: same path under the enforced AppArmor profile (ubuntu only —
      # one full guestkit pass is enough to catch profile regressions).
      - name: AppArmor-enforced build-image smoke
        if: matrix.distro == 'ubuntu'
        working-directory: fluxvm
        env:
          FLUXVM_BIN: ${{ github.workspace }}/fluxvm/target/release/fluxctl
          TEST_PACKAGE: tree
          TEST_SERVICE: ${{ matrix.test_service }}
        run: sudo -E ./scripts/test-apparmor-build-image.sh --image /tmp/base-image.qcow2

  container-image:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - name: Checkout fluxvm
        uses: actions/checkout@v7
        with:
          path: fluxvm

      - name: Checkout guestkit (BuildKit context)
        uses: actions/checkout@v7
        with:
          repository: zyvorai/guestkit
          path: guestkit

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v4

      - name: Build image (no push on PR)
        uses: docker/build-push-action@v7
        with:
          context: fluxvm
          build-contexts: guestkit=guestkit
          push: false
          tags: ghcr.io/zyvorai/fluxvm:ci
          cache-from: type=gha
          cache-to: type=gha,mode=max

  website:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7

      - uses: actions/setup-node@v7
        with:
          node-version: "22"
          cache: npm
          cache-dependency-path: website/package-lock.json

      - name: Install dependencies
        run: cd website && npm ci

      - name: Build
        run: cd website && npm run build
