name: Build debug tools

# Builds the `service_debug` helper binaries (wal_inspector, segment_inspector, ...)
# and uploads them to a GCS bucket via the S3-compatible XML API. These are debug-only
# tools built between releases; they are intentionally NOT part of the release artifacts.
#
# Built for x86_64-unknown-linux-musl so the binaries are statically linked and run on
# any Linux host (glibc or musl, any distro).
#
# Download (public):
#   curl -LO https://storage.googleapis.com/<bucket>/debug-tools/dev/wal_inspector

on:
  push:
    branches: [ dev ]
  workflow_dispatch:
    inputs:
      branch:
        description: 'Branch to build the debug tools from'
        required: true
        default: dev

permissions:
  contents: read

env:
  CARGO_TERM_COLOR: always
  RUSTC_WRAPPER: sccache
  SCCACHE_GHA_ENABLED: "true"
  # Tools in the main `qdrant` crate, gated behind its `service_debug` feature.
  # Keep this list in sync with the `service_debug` [[bin]] targets in Cargo.toml
  DEBUG_BINS: wal_inspector wal_pop segment_inspector schema_generator model_testing
  # Standalone tool crates (own workspace member), built with `-p`.
  # Bin name must match the crate name. Built in the same invocation as the
  # qdrant bins so Cargo unifies features and shared workspace crates compile once.
  EDGE_BINS: edge-shard-query
  # Static target so the binaries run everywhere
  TARGET: x86_64-unknown-linux-musl
  # Faster to compile than release (no fat LTO); still opt-level 3. See [profile.perf].
  PROFILE: perf
  # ---- Adjust to your GCS bucket ----
  GCS_BUCKET: qdrant-debug
  GCS_ENDPOINT: https://storage.googleapis.com
  # GCS interop ignores the region, but the AWS CLI requires one to be set.
  AWS_REGION: auto

jobs:
  build-debug-tools:
    runs-on: ubuntu-latest
    steps:
      - name: Install build dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y gcc-multilib clang cmake protobuf-compiler
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          # `inputs.branch` on manual dispatch; the pushed branch otherwise
          ref: ${{ inputs.branch || github.ref_name }}
      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
        with:
          targets: x86_64-unknown-linux-musl
      - name: Install Protoc
        uses: ./.github/actions/setup-protoc
      - name: Install cross-compilation tools
        uses: taiki-e/setup-cross-toolchain-action@3d9770ce98eb7dbcf378563182a5e8031165f75b # v1.41.0
        with:
          target: x86_64-unknown-linux-musl
      - name: Setup sccache
        uses: mozilla-actions/sccache-action@9e7fa8a12102821edf02ca5dbea1acd0f89a2696 # v0.0.10
      - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
        with:
          # Namespace cache per target/profile so musl perf doesn't collide with gnu/release caches
          key: ${{ env.TARGET }}-${{ env.PROFILE }}
          # Only save the cache on dev; feature-branch caches would just get evicted under the 10 GB budget
          save-if: ${{ (inputs.branch || github.ref_name) == 'dev' }}

      - name: Build debug tools
        run: |
          # Single build with explicit `--package qdrant` so `--features service_debug`
          # applies only to qdrant (not edge tools, which lack that feature).
          cargo build --profile "$PROFILE" --locked --features service_debug \
            --target "$TARGET" \
            --package qdrant \
            $(printf -- '--bin %s ' $DEBUG_BINS) \
            $(for bin in $EDGE_BINS; do printf -- '--package %s --bin %s ' "$bin" "$bin"; done)
          sccache --show-stats

      - name: Collect binaries
        run: |
          mkdir -p debug-tools
          for bin in $DEBUG_BINS $EDGE_BINS; do
            cp "target/$TARGET/$PROFILE/$bin" debug-tools/
          done

      - name: Upload to GCS
        env:
          # GCS HMAC interoperability keys (AWS-style), stored as repo secrets.
          AWS_ACCESS_KEY_ID: ${{ secrets.GCS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.GCS_SECRET_ACCESS_KEY }}
          BRANCH: ${{ inputs.branch || github.ref_name }}
          # Recent AWS CLI adds default CRC32 integrity checksums that GCS's
          # S3-compatible UploadPart rejects (SignatureDoesNotMatch). Opt out so
          # multipart uploads of the larger binaries work.
          AWS_REQUEST_CHECKSUM_CALCULATION: when_required
          AWS_RESPONSE_CHECKSUM_VALIDATION: when_required
        run: |
          branch="${BRANCH//\//-}" # replace all / with -
          sha="$(git rev-parse HEAD)" # actual commit built from the chosen branch
          base="${GCS_ENDPOINT}/${GCS_BUCKET}/debug-tools"

          # Upload under a moving branch prefix (always latest) and an immutable
          # per-commit prefix (history). Public read is granted at the bucket/IAM
          # level (allUsers -> Storage Object Viewer), so no per-object ACL here.
          for prefix in "$branch" "$branch-${sha}"; do
            aws s3 cp debug-tools/ "s3://${GCS_BUCKET}/debug-tools/${prefix}/" \
              --recursive --endpoint-url "$GCS_ENDPOINT" --region "$AWS_REGION"
          done

          # Surface direct links in the job summary
          echo "## Debug tools uploaded" >> "$GITHUB_STEP_SUMMARY"
          echo "Latest (\`$branch\`):" >> "$GITHUB_STEP_SUMMARY"
          for bin in $DEBUG_BINS $EDGE_BINS; do
            echo "- [\`$bin\`]($base/$branch/$bin)" >> "$GITHUB_STEP_SUMMARY"
          done
          echo "Immutable: \`$base/$branch-${sha}/<bin>\`" >> "$GITHUB_STEP_SUMMARY"
