name: Build and Release

on:
  push:
    tags:
      - "v*"

permissions:
  contents: write

env:
  LINUX_TARGET: x86_64-unknown-linux-musl
  WINDOWS_TARGET: x86_64-pc-windows-gnu
  PGO_SITEMAP: https://riptide-pgo.blitzw.in/sitemap.xml

jobs:
  build-linux:
    name: Build Linux musl binary with PGO
    runs-on: self-hosted

    container:
      image: rust:alpine

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Install dependencies
        run: |
          apk add --no-cache \
            build-base \
            musl-dev \
            curl \
            file \
            binutils \
            coreutils

      - name: Install Rust targets and LLVM tools
        run: |
          rustup target add "$LINUX_TARGET"
          rustup component add llvm-tools-preview

      - name: Locate LLVM tools
        id: llvm
        shell: sh
        run: |
          SYSROOT="$(rustc --print sysroot)"
          HOST="$(rustc -vV | sed -n 's/^host: //p')"
          LLVM_BIN="$SYSROOT/lib/rustlib/$HOST/bin"

          test -x "$LLVM_BIN/llvm-profdata"

          echo "LLVM_BIN=$LLVM_BIN" >> "$GITHUB_OUTPUT"

      - name: Build instrumented binary
        shell: sh
        run: |
          rm -rf /tmp/riptide-pgo
          mkdir -p /tmp/riptide-pgo

          RUSTFLAGS="\
            -C target-feature=+crt-static \
            -C profile-generate=/tmp/riptide-pgo \
          " cargo build \
            --release \
            --target "$LINUX_TARGET"

          cp \
            "target/$LINUX_TARGET/release/riptide" \
            /tmp/riptide-pgo/riptide

          chmod +x /tmp/riptide-pgo/riptide

          file /tmp/riptide-pgo/riptide

      - name: Train PGO profile
        shell: sh
        env:
          LLVM_PROFILE_FILE: /tmp/riptide-pgo/%m-%p.profraw
        run: |
          timeout 10m \
            /tmp/riptide-pgo/riptide \
              --sitemap "$PGO_SITEMAP" \
              --output /tmp/pgo-urls.txt \
              --scheme https \
              --workers 16 \
              --timeout-secs 30

          test -s /tmp/pgo-urls.txt

          echo "Training input produced:"
          wc -l /tmp/pgo-urls.txt

          echo "Generated profiles:"
          find /tmp/riptide-pgo -name '*.profraw' -type f -print

      - name: Merge PGO profiles
        shell: sh
        run: |
          "${{ steps.llvm.outputs.LLVM_BIN }}/llvm-profdata" merge \
            -o /tmp/riptide-pgo.profdata \
            /tmp/riptide-pgo/*.profraw

          test -s /tmp/riptide-pgo.profdata

      - name: Build optimized Linux binary
        shell: sh
        run: |
          RUSTFLAGS="\
            -C target-feature=+crt-static \
            -C profile-use=/tmp/riptide-pgo.profdata \
          " cargo build \
            --release \
            --target "$LINUX_TARGET"

          mkdir -p dist

          cp \
            "target/$LINUX_TARGET/release/riptide" \
            dist/riptide

          chmod +x dist/riptide

      - name: Verify Linux binary
        shell: sh
        run: |
          file dist/riptide

          if readelf -l dist/riptide | grep -q 'INTERP'; then
            echo "ERROR: riptide has a dynamic ELF interpreter"
            exit 1
          fi

          echo "Static musl binary verified."

      - name: Smoke test
        run: |
          ./dist/riptide --help

      - name: Upload Linux binary
        uses: actions/upload-artifact@v4
        with:
          name: riptide-linux
          path: dist/riptide

  build-windows:
    name: Build Windows binary
    runs-on: self-hosted

    container:
      image: rust:alpine

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Install Windows cross-compiler
        run: |
          apk add --no-cache \
            mingw-w64-gcc \
            build-base \
            file

      - name: Install Rust target
        run: |
          rustup target add "$WINDOWS_TARGET"

      - name: Build Windows binary
        shell: sh
        run: |
          cargo build \
            --release \
            --target "$WINDOWS_TARGET"

          mkdir -p dist

          cp \
            "target/$WINDOWS_TARGET/release/riptide.exe" \
            dist/riptide.exe

      - name: Verify Windows binary
        shell: sh
        run: |
          file dist/riptide.exe

      - name: Smoke test
        shell: sh
        run: |
          test -s dist/riptide.exe

      - name: Upload Windows binary
        uses: actions/upload-artifact@v4
        with:
          name: riptide-windows
          path: dist/riptide.exe

  release:
    name: Create Forgejo release
    runs-on: self-hosted
    needs:
      - build-linux
      - build-windows

    container:
      image: alpine:latest

    steps:
      - name: Install release tools
        run: |
          apk add --no-cache \
            curl \
            jq \
            coreutils

      - name: Download Linux artifact
        uses: actions/download-artifact@v4
        with:
          name: riptide-linux
          path: release

      - name: Download Windows artifact
        uses: actions/download-artifact@v4
        with:
          name: riptide-windows
          path: release

      - name: Make binaries executable
        run: |
          chmod 755 release/riptide
          chmod 644 release/riptide.exe

      - name: Calculate checksums
        id: checksum
        shell: sh
        run: |
          LINUX_SHA="$(sha256sum release/riptide | awk '{print $1}')"
          WINDOWS_SHA="$(sha256sum release/riptide.exe | awk '{print $1}')"

          printf '%s\n' "$LINUX_SHA"
          printf '%s\n' "$WINDOWS_SHA"

          {
            echo "LINUX_SHA=$LINUX_SHA"
            echo "WINDOWS_SHA=$WINDOWS_SHA"
          } >> "$GITHUB_OUTPUT"

      - name: Create release
        shell: sh
        env:
          FORGEJO_TOKEN: ${{ secrets.FORGEJO_TOKEN }}
          REPO: ${{ github.repository }}
          TAG: ${{ github.ref_name }}
          LINUX_SHA: ${{ steps.checksum.outputs.LINUX_SHA }}
          WINDOWS_SHA: ${{ steps.checksum.outputs.WINDOWS_SHA }}
        run: |
          cat > release.json <<EOF
          {
            "tag_name": "$TAG",
            "name": "$TAG",
            "body": "initial release\nriptide: static linux musl binary (x86_64).\nriptide.exe: windows binary\n\n$LINUX_SHA  riptide\n$WINDOWS_SHA  riptide.exe",
            "draft": false,
            "prerelease": false
          }
          EOF

          RELEASE_JSON="$(
            curl -fsSL \
              -X POST \
              -H "Authorization: token $FORGEJO_TOKEN" \
              -H "Content-Type: application/json" \
              "https://codeberg.org/api/v1/repos/$REPO/releases" \
              --data-binary @release.json
          )"

          RELEASE_ID="$(printf '%s' "$RELEASE_JSON" | jq -r '.id')"

          test "$RELEASE_ID" != "null"
          test -n "$RELEASE_ID"

          echo "Created release ID: $RELEASE_ID"

      - name: Upload Linux binary
        shell: sh
        env:
          FORGEJO_TOKEN: ${{ secrets.FORGEJO_TOKEN }}
          REPO: ${{ github.repository }}
          TAG: ${{ github.ref_name }}
        run: |
          curl -fsSL \
            -X POST \
            -H "Authorization: token $FORGEJO_TOKEN" \
            -H "Content-Type: application/octet-stream" \
            --data-binary @release/riptide \
            "https://codeberg.org/api/v1/repos/$REPO/releases/$( \
              curl -fsSL \
                -H "Authorization: token $FORGEJO_TOKEN" \
                "https://codeberg.org/api/v1/repos/$REPO/releases/tags/$TAG" |
              jq -r '.id'
            )/assets?name=riptide"

      - name: Upload Windows binary
        shell: sh
        env:
          FORGEJO_TOKEN: ${{ secrets.FORGEJO_TOKEN }}
          REPO: ${{ github.repository }}
          TAG: ${{ github.ref_name }}
        run: |
          curl -fsSL \
            -X POST \
            -H "Authorization: token $FORGEJO_TOKEN" \
            -H "Content-Type: application/octet-stream" \
            --data-binary @release/riptide.exe \
            "https://codeberg.org/api/v1/repos/$REPO/releases/$( \
              curl -fsSL \
                -H "Authorization: token $FORGEJO_TOKEN" \
                "https://codeberg.org/api/v1/repos/$REPO/releases/tags/$TAG" |
              jq -r '.id'
            )/assets?name=riptide.exe"