name: Pull Request

on:
  pull_request:
    types: [labeled, opened, synchronize, reopened]

# Cancel in‑flight runs for the same PR when a new push arrives.
concurrency:
  group: pr-${{ github.workflow }}-${{ github.event.pull_request.number }}
  cancel-in-progress: true

jobs:
  build:
    name: Build
    runs-on: [self-hosted]
    permissions:
      contents: read
      packages: write

    # Never run when the PR carries the "release" label.
    if: |
      !contains(github.event.pull_request.labels.*.name, 'release')

    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          fetch-tags: true

      - name: Login to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}
          registry: ghcr.io
        env:
          DOCKER_CONFIG: ${{ runner.temp }}/.docker

      - name: Build
        run: make build
        env:
          DOCKER_CONFIG: ${{ runner.temp }}/.docker

      - name: Build Talos image
        run: make -C packages/core/installer talos-nocloud
     
      - name: Upload installer
        uses: actions/upload-artifact@v4
        with:
          name: cozystack-installer
          path: _out/assets/cozystack-installer.yaml

      - name: Upload Talos image
        uses: actions/upload-artifact@v4
        with:
          name: talos-image
          path: _out/assets/nocloud-amd64.raw.xz

  resolve_assets:
    name: "Resolve assets"
    runs-on: ubuntu-latest
    if: contains(github.event.pull_request.labels.*.name, 'release')
    outputs:
     installer_id: ${{ steps.fetch_assets.outputs.installer_id }}
     disk_id:      ${{ steps.fetch_assets.outputs.disk_id }}

    steps:
      - name: Checkout code
        if: contains(github.event.pull_request.labels.*.name, 'release')
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          fetch-tags: true

      - name: Extract tag from PR branch (release PR)
        if: contains(github.event.pull_request.labels.*.name, 'release')
        id: get_tag
        uses: actions/github-script@v7
        with:
          script: |
            const branch = context.payload.pull_request.head.ref;
            const m = branch.match(/^release-(\d+\.\d+\.\d+(?:[-\w\.]+)?)$/);
            if (!m) {
              core.setFailed(`❌ Branch '${branch}' does not match 'release-X.Y.Z[-suffix]'`);
              return;
            }
            core.setOutput('tag', `v${m[1]}`);

      - name: Find draft release & asset IDs (release PR)
        if: contains(github.event.pull_request.labels.*.name, 'release')
        id: fetch_assets
        uses: actions/github-script@v7
        with:
          github-token: ${{ secrets.GH_PAT }}
          script: |
            const tag = '${{ steps.get_tag.outputs.tag }}';
            const releases = await github.rest.repos.listReleases({
              owner: context.repo.owner,
              repo:  context.repo.repo,
              per_page: 100
            });
            const draft = releases.data.find(r => r.tag_name === tag && r.draft);
            if (!draft) {
              core.setFailed(`Draft release '${tag}' not found`);
              return;
            }
            const find = (n) => draft.assets.find(a => a.name === n)?.id;
            const installerId = find('cozystack-installer.yaml');
            const diskId      = find('nocloud-amd64.raw.xz');
            if (!installerId || !diskId) {
              core.setFailed('Required assets missing in draft release');
              return;
            }
            core.setOutput('installer_id', installerId);
            core.setOutput('disk_id',      diskId);


  prepare_env:
    name: "Prepare environment"
    runs-on: [self-hosted]
    permissions:
      contents: read
      packages: read
    needs: ["build", "resolve_assets"]
    if: ${{ always() && (needs.build.result == 'success' || needs.resolve_assets.result == 'success') }}

    steps:
      # ▸ Regular PR path – download artefacts produced by the *build* job
      - name: "Download Talos image (regular PR)"
        if: "!contains(github.event.pull_request.labels.*.name, 'release')"
        uses: actions/download-artifact@v4
        with:
          name: talos-image
          path: _out/assets


      # ▸ Release PR path – fetch artefacts from the corresponding draft release
      - name: Download assets from draft release (release PR)
        if: contains(github.event.pull_request.labels.*.name, 'release')
        run: |
          curl -sSL -H "Authorization: token ${GH_PAT}" -H "Accept: application/octet-stream" \
            -o _out/assets/nocloud-amd64.raw.xz \
            "https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/assets/${{ needs.resolve_assets.outputs.disk_id }}"
        env:
          GH_PAT: ${{ secrets.GH_PAT }}

      # ▸ Start actual job steps
      - name: Set sandbox ID
        run: echo "SANDBOX_NAME=cozy-e2e-sandbox-$(echo "${GITHUB_REPOSITORY}:${GITHUB_WORKFLOW}:${GITHUB_REF}" | sha256sum | cut -c1-10)" >> $GITHUB_ENV

      - name: Prepare workspace
        run: |
          cd ..
          rm -rf /tmp/$SANDBOX_NAME
          cp -r cozystack /tmp/$SANDBOX_NAME
          sudo systemctl stop "rm-workspace-$SANDBOX_NAME.timer" "rm-workspace-$SANDBOX_NAME.service" 2>/dev/null || true
          sudo systemctl reset-failed "rm-workspace-$SANDBOX_NAME.timer" "rm-workspace-$SANDBOX_NAME.service" 2>/dev/null || true
          sudo systemctl daemon-reexec
          sudo systemd-run \
            --on-calendar="$(date -d 'now + 24 hours' '+%Y-%m-%d %H:%M:%S')" \
            --unit=rm-workspace-$SANDBOX_NAME \
            rm -rf /tmp/$SANDBOX_NAME
            
      - name: Prepare environment
        run: |
          cd /tmp/$SANDBOX_NAME
          make SANDBOX_NAME=$SANDBOX_NAME prepare-env

  install_cozystack:
    name: "Install Cozystack"
    runs-on: [self-hosted]
    permissions:
      contents: read
      packages: read
    needs: ["prepare_env", "resolve_assets"]
    if: ${{ always() && needs.prepare_env.result == 'success' }}

    steps:
      - name: Prepare _out/assets directory
        run: mkdir -p _out/assets

      # ▸ Regular PR path – download artefacts produced by the *build* job
      - name: "Download installer (regular PR)"
        if: "!contains(github.event.pull_request.labels.*.name, 'release')"
        uses: actions/download-artifact@v4
        with:
          name: cozystack-installer
          path: _out/assets

      # ▸ Release PR path – fetch artefacts from the corresponding draft release
      - name: Download assets from draft release (release PR)
        if: contains(github.event.pull_request.labels.*.name, 'release')
        run: |
          curl -sSL -H "Authorization: token ${GH_PAT}" -H "Accept: application/octet-stream" \
            -o _out/assets/cozystack-installer.yaml \
            "https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/assets/${{ needs.resolve_assets.outputs.installer_id }}"
        env:
          GH_PAT: ${{ secrets.GH_PAT }}
 
      # ▸ Start actual job steps
      - name: Set sandbox ID
        run: echo "SANDBOX_NAME=cozy-e2e-sandbox-$(echo "${GITHUB_REPOSITORY}:${GITHUB_WORKFLOW}:${GITHUB_REF}" | sha256sum | cut -c1-10)" >> $GITHUB_ENV

      - name: Install Cozystack into sandbox
        run: |
          cd /tmp/$SANDBOX_NAME
          make -C packages/core/testing SANDBOX_NAME=$SANDBOX_NAME install-cozystack

  detect_test_matrix:
    name: "Detect e2e test matrix"
    runs-on: ubuntu-latest
    outputs:
      matrix: ${{ steps.set.outputs.matrix }}

    steps:
      - uses: actions/checkout@v4
      - id: set
        run: |
          apps=$(find hack/e2e-apps -maxdepth 1 -mindepth 1 -name '*.bats' | \
            awk -F/ '{sub(/\..+/, "", $NF); print $NF}' | jq -R . | jq -cs .)
          echo "matrix={\"app\":$apps}" >> "$GITHUB_OUTPUT"

  test_apps:
    strategy:
      matrix: ${{ fromJson(needs.detect_test_matrix.outputs.matrix) }}
    name: Test ${{ matrix.app }}
    runs-on: [self-hosted]
    needs: [install_cozystack,detect_test_matrix]
    if: ${{ always() && (needs.install_cozystack.result == 'success' && needs.detect_test_matrix.result == 'success') }}

    steps:
      - name: Set sandbox ID
        run: echo "SANDBOX_NAME=cozy-e2e-sandbox-$(echo "${GITHUB_REPOSITORY}:${GITHUB_WORKFLOW}:${GITHUB_REF}" | sha256sum | cut -c1-10)" >> $GITHUB_ENV

      - name: E2E Apps
        run: |
          cd /tmp/$SANDBOX_NAME
          make -C packages/core/testing SANDBOX_NAME=$SANDBOX_NAME test-apps-${{ matrix.app }}

  cleanup:
    name: Tear down environment
    runs-on: [self-hosted]
    needs: test_apps
    if: ${{ always() && needs.test_apps.result == 'success' }}

    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          fetch-tags: true

      - name: Set sandbox ID
        run: echo "SANDBOX_NAME=cozy-e2e-sandbox-$(echo "${GITHUB_REPOSITORY}:${GITHUB_WORKFLOW}:${GITHUB_REF}" | sha256sum | cut -c1-10)" >> $GITHUB_ENV

      - name: Tear down sandbox
        run: make -C packages/core/testing SANDBOX_NAME=$SANDBOX_NAME delete

      - name: Remove workspace
        run: rm -rf /tmp/$SANDBOX_NAME

      - name: Tear down timers
        run: |
          sudo systemctl stop "rm-workspace-$SANDBOX_NAME.timer" "rm-workspace-$SANDBOX_NAME.service" 2>/dev/null || true
          sudo systemctl reset-failed "rm-workspace-$SANDBOX_NAME.timer" "rm-workspace-$SANDBOX_NAME.service" 2>/dev/null || true
          sudo systemctl stop "teardown-$SANDBOX_NAME.timer" "teardown-$SANDBOX_NAME.service" 2>/dev/null || true
          sudo systemctl reset-failed "teardown-$SANDBOX_NAME.timer" "teardown-$SANDBOX_NAME.service" 2>/dev/null || true
          sudo systemctl daemon-reexec
