name: Test snapshot

on:
  workflow_run:
    workflows:
      - Snapshot
    types:
      - completed

jobs:
  integration:
    runs-on: ubuntu-latest
    if: ${{ github.event.workflow_run.conclusion == 'success' }}
    timeout-minutes: 30
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: fedora
            url: https://download.fedoraproject.org/pub/fedora/linux/releases/
          - os: ubuntu
            url: https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img
          - os: debian
            url: https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2
    steps:
      - name: Download artifact
        uses: actions/github-script@v8
        with:
          script: |
            var artifacts = await github.rest.actions.listWorkflowRunArtifacts({
              owner: context.repo.owner,
              repo: context.repo.repo,
              run_id: ${{github.event.workflow_run.id }},
            });
            var matchArtifact = artifacts.data.artifacts.filter((artifact) => {
              return artifact.name == "dist"
            })[0];
            var download = await github.rest.actions.downloadArtifact({
              owner: context.repo.owner,
              repo: context.repo.repo,
              artifact_id: matchArtifact.id,
              archive_format: 'zip',
            });
            var fs = require('fs');
            fs.writeFileSync('${{github.workspace}}/dist.zip', Buffer.from(download.data));
      - name: Unzip artifact
        run: |
          unzip dist.zip
      - name: Advertise check in PR
        id: initcheck
        uses: actions/github-script@v8
        with:
          script: |
            const fs = require('fs'),
                  sha = fs.readFileSync('SHA', 'utf8'),
                  args = {
                    owner: context.repo.owner,
                    repo: context.repo.repo,
                    head_sha: sha,
                    name: 'Test snapshot on ${{ matrix.os }}',
                    status: 'in_progress'
                  };
            console.log("Arguments:", args);
            let result = await github.rest.checks.create(args);
            console.log("Result:", result);
            return {
              check_run_id: result.data.id
            };
      - name: Cache cloud image
        uses: actions/cache@v5
        with:
          path: ${{ matrix.os }}.img
          key: snapshot-test-v3-${{ matrix.os }}
      - name: Install QEMU
        run: |
          sudo apt -qyy update > /dev/null
          sudo apt -qyy install qemu-system-x86 qemu-utils curl genisoimage
      - name: Download ${{ matrix.os }} image
        run: |
          if [ ! -f "${{ matrix.os }}.img" ]; then
            if [ "${{ matrix.os }}" = "fedora" ]; then
              fedora_release="$(
                curl -fsSL "${{ matrix.url }}" \
                  | sed -n 's/.*href="\([0-9][0-9]*\)\/".*/\1/p' \
                  | sort -V \
                  | sed -n '$p'
              )"
              [ -n "$fedora_release" ] || { echo "Unable to resolve latest Fedora release"; exit 1; }
              fedora_images_url="${{ matrix.url }}${fedora_release}/Cloud/x86_64/images/"
              fedora_img="$(
                curl -fsSL "$fedora_images_url" \
                  | sed -n 's/.*href="\([^"]*Fedora-Cloud-Base-Generic-[0-9][^"]*\.x86_64\.qcow2\)".*/\1/p' \
                  | sort -Vu \
                  | sed -n '$p'
              )"
              [ -n "$fedora_img" ] || { echo "Unable to resolve latest Fedora qcow2 image"; exit 1; }
              curl -fL --retry 3 --retry-delay 2 -o "${{ matrix.os }}.img" "$fedora_images_url$fedora_img"
            else
              curl -fL --retry 3 --retry-delay 2 -o "${{ matrix.os }}.img" "${{ matrix.url }}"
            fi
          fi
          qemu-img create -b ${{ matrix.os }}.img -F qcow2 -f qcow2 cloud.img 20G
      - name: Create test script
        run: |
          cat <<'EOF' > test
          #!/bin/sh
          set -e

          # Install NextDNS
          . /etc/os-release
          case $ID in
            ubuntu|debian)
              dpkg -i /data/nextdns*_linux_amd64.deb
              ;;
            fedora)
              rpm -ivh /data/nextdns*_linux_amd64.rpm
              ;;
          esac

          # Do some tests
          nextdns status
          nextdns activate
          nextdns config
          nextdns log
          getent hosts test.nextdns.io
          getent hosts nextdns.io

          # End of script
          echo "__NEXTDNS_TEST_OK__" | tee -a /data/output
          echo OK
          EOF
          chmod +x test
      - name: Run test script
        run: |
          set -euo pipefail
          rm -rf share
          mkdir -p share
          cp test share/
          for pkg in nextdns*_linux_amd64.*; do
            [ -e "$pkg" ] || continue
            cp "$pkg" share/
          done
          cat <<'EOF' > user-data
          #cloud-config
          password: .Linux.
          chpasswd:
            expire: False
          runcmd:
            - mkdir /data
            - sh -c 'for dev in /dev/vdb1 /dev/vdb /dev/sdb1 /dev/sdb; do [ -b "$dev" ] || continue; mount -n -t vfat "$dev" /data && break; done'
            - sh -c 'mountpoint -q /data || { echo "Unable to mount shared disk on /data"; exit 1; }'
            - /data/test 2>&1 | tee /data/output
            - sh -c 'sync; sync; (poweroff -f || shutdown -P now || systemctl poweroff || echo b > /proc/sysrq-trigger)'
          EOF
          cat <<EOF > meta-data
          local-hostname: nextdns
          EOF
          genisoimage -output metadata.iso -volid cidata -joliet -rock user-data meta-data
          accel="tcg,thread=multi"
          if [ -r /dev/kvm ] && [ -w /dev/kvm ]; then
            accel="kvm"
          fi
          echo "Using QEMU accel: $accel"
          qemu_status=0
          success=0
          deadline=$((SECONDS + 1200))
          next_report=$((SECONDS + 30))
          qemu-system-x86_64 \
            -accel "$accel" \
            -no-user-config -nodefaults \
            -display none \
            -smp 2 \
            -m 1024M \
            -device virtio-rng \
            -chardev stdio,id=charserial0,signal=off \
            -device isa-serial,chardev=charserial0,id=serial0 \
            -boot c \
            -drive file=cloud.img,if=virtio,media=disk \
            -drive file=metadata.iso,format=raw,if=ide,media=cdrom,read-only=on \
            -drive file=fat:rw:share,format=raw,if=virtio,media=disk \
            -netdev user,id=internet \
            -device virtio-net-pci,netdev=internet \
            -no-reboot > qemu.log 2>&1 &
          qemu_pid=$!
          stop_qemu() {
            if ! kill -0 "$qemu_pid" 2>/dev/null; then
              return
            fi
            kill -TERM "$qemu_pid" 2>/dev/null || true
            for _ in 1 2 3 4 5 6 7 8 9 10; do
              if ! kill -0 "$qemu_pid" 2>/dev/null; then
                return
              fi
              sleep 1
            done
            kill -KILL "$qemu_pid" 2>/dev/null || true
          }
          while kill -0 "$qemu_pid" 2>/dev/null; do
            if grep -q "__NEXTDNS_TEST_OK__" qemu.log || ([ -f share/output ] && grep -q "__NEXTDNS_TEST_OK__" share/output); then
              success=1
              echo "Guest test marker detected while VM is running; stopping QEMU."
              stop_qemu
              break
            fi
            if [ "$SECONDS" -ge "$deadline" ]; then
              qemu_status=124
              echo "QEMU timed out after 20m while waiting for guest tests to complete."
              stop_qemu
              break
            fi
            if [ "$SECONDS" -ge "$next_report" ]; then
              echo "Still waiting for guest test marker..."
              tail -n 20 qemu.log || true
              next_report=$((SECONDS + 30))
            fi
            sleep 2
          done
          if kill -0 "$qemu_pid" 2>/dev/null; then
            stop_qemu
          fi
          wait "$qemu_pid" || qemu_status=$?
          if [ "$success" -eq 1 ]; then
            qemu_status=0
          fi

          if [ "$qemu_status" -eq 124 ]; then
            echo "QEMU timed out after 20m while waiting for guest tests to complete."
          fi

          echo "==== QEMU serial output (last 200 lines) ===="
          tail -n 200 qemu.log || true

          if [ -f share/output ]; then
            echo "==== Guest output (last 200 lines) ===="
            tail -n 200 share/output
          else
            echo "Guest never produced /data/output (cloud-init/test script may not have run)."
          fi

          if grep -q "__NEXTDNS_TEST_OK__" qemu.log || ([ -f share/output ] && grep -q "__NEXTDNS_TEST_OK__" share/output); then
            echo "Guest test marker detected."
            exit 0
          fi
          echo "Guest test marker not detected."
          exit 1
      - name: Advertise check results in PR
        uses: actions/github-script@v8
        if: always()
        with:
          script: |
            let args = {
              owner: context.repo.owner,
              repo: context.repo.repo,
              check_run_id: ${{ fromJSON(steps.initcheck.outputs.result).check_run_id }},
              status: 'completed',
              conclusion: '${{ job.status }}'
            };
            console.log("Arguments:", args);
            let result = await github.rest.checks.update(args);
            console.log("Result:", result);
