name: Container Registry Prune

on:
  schedule:
    - cron: '0 3 * * 0' # Weekly: Sundays at 03:00 UTC
  workflow_dispatch:
    inputs:
      keep_days:
        description: 'Number of days to retain images (unprotected)'
        required: false
        default: '30'
      dry_run:
        description: 'If true, only logs candidates and does not delete (default: false for active cleanup)'
        required: false
        default: 'false'
      keep_last_n:
        description: 'Keep last N newest images (global)'
        required: false
        default: '30'

permissions:
  packages: write
  contents: read

jobs:
  prune-ghcr:
    runs-on: ubuntu-latest
    env:
      OWNER: ${{ github.repository_owner }}
      IMAGE_NAME: charon
      KEEP_DAYS: ${{ github.event.inputs.keep_days || '30' }}
      KEEP_LAST_N: ${{ github.event.inputs.keep_last_n || '30' }}
      DRY_RUN: ${{ github.event_name == 'pull_request' && 'true' || github.event.inputs.dry_run || 'false' }}
      PROTECTED_REGEX: '["^v?[0-9]+\\.[0-9]+\\.[0-9]+$","^latest$","^main$","^develop$"]'
      PRUNE_UNTAGGED: 'true'
      PRUNE_SBOM_TAGS: 'true'
    steps:
      - name: Checkout
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

      - name: Install tools
        run: |
          sudo apt-get update && sudo apt-get install -y jq curl

      - name: Run GHCR prune
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          chmod +x scripts/prune-ghcr.sh
          ./scripts/prune-ghcr.sh 2>&1 | tee prune-ghcr-${{ github.run_id }}.log

      - name: Summarize GHCR results
        if: always()
        run: |
          set -euo pipefail
          SUMMARY_FILE=prune-summary-ghcr.env
          LOG_FILE=prune-ghcr-${{ github.run_id }}.log

          human() {
            local bytes=${1:-0}
            if [ -z "$bytes" ] || [ "$bytes" -eq 0 ]; then
              echo "0 B"
              return
            fi
            awk -v b="$bytes" 'BEGIN { split("B KiB MiB GiB TiB",u," "); i=0; x=b; while(x>1024){x/=1024;i++} printf "%0.2f %s", x, u[i+1] }'
          }

          if [ -f "$SUMMARY_FILE" ]; then
            TOTAL_CANDIDATES=$(grep -E '^TOTAL_CANDIDATES=' "$SUMMARY_FILE" | cut -d= -f2 || echo 0)
            TOTAL_CANDIDATES_BYTES=$(grep -E '^TOTAL_CANDIDATES_BYTES=' "$SUMMARY_FILE" | cut -d= -f2 || echo 0)
            TOTAL_DELETED=$(grep -E '^TOTAL_DELETED=' "$SUMMARY_FILE" | cut -d= -f2 || echo 0)
            TOTAL_DELETED_BYTES=$(grep -E '^TOTAL_DELETED_BYTES=' "$SUMMARY_FILE" | cut -d= -f2 || echo 0)

            {
              echo "## GHCR prune summary"
              echo "- candidates: ${TOTAL_CANDIDATES} (≈ $(human "${TOTAL_CANDIDATES_BYTES}"))"
              echo "- deleted: ${TOTAL_DELETED} (≈ $(human "${TOTAL_DELETED_BYTES}"))"
            } >> "$GITHUB_STEP_SUMMARY"
          else
            deleted_bytes=$(grep -oE '\( *approx +[0-9]+ bytes\)' "$LOG_FILE" | sed -E 's/.*approx +([0-9]+) bytes.*/\1/' | awk '{s+=$1} END {print s+0}' || true)
            deleted_count=$(grep -cE 'deleting |DRY RUN: would delete' "$LOG_FILE" || true)

            {
              echo "## GHCR prune summary"
              echo "- deleted (approx): ${deleted_count} (≈ $(human "${deleted_bytes}"))"
            } >> "$GITHUB_STEP_SUMMARY"
          fi

      - name: Upload GHCR prune artifacts
        if: always()
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
        with:
          name: prune-ghcr-log-${{ github.run_id }}
          path: |
            prune-ghcr-${{ github.run_id }}.log
            prune-summary-ghcr.env

  prune-dockerhub:
    runs-on: ubuntu-latest
    env:
      OWNER: ${{ github.repository_owner }}
      IMAGE_NAME: charon
      KEEP_DAYS: ${{ github.event.inputs.keep_days || '30' }}
      KEEP_LAST_N: ${{ github.event.inputs.keep_last_n || '30' }}
      DRY_RUN: ${{ github.event_name == 'pull_request' && 'true' || github.event.inputs.dry_run || 'false' }}
      PROTECTED_REGEX: '["^v?[0-9]+\\.[0-9]+\\.[0-9]+$","^latest$","^main$","^develop$"]'
    steps:
      - name: Checkout
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

      - name: Install tools
        run: |
          sudo apt-get update && sudo apt-get install -y jq curl

      - name: Run Docker Hub prune
        env:
          DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
          DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
        run: |
          chmod +x scripts/prune-dockerhub.sh
          ./scripts/prune-dockerhub.sh 2>&1 | tee prune-dockerhub-${{ github.run_id }}.log

      - name: Summarize Docker Hub results
        if: always()
        run: |
          set -euo pipefail
          SUMMARY_FILE=prune-summary-dockerhub.env
          LOG_FILE=prune-dockerhub-${{ github.run_id }}.log

          human() {
            local bytes=${1:-0}
            if [ -z "$bytes" ] || [ "$bytes" -eq 0 ]; then
              echo "0 B"
              return
            fi
            awk -v b="$bytes" 'BEGIN { split("B KiB MiB GiB TiB",u," "); i=0; x=b; while(x>1024){x/=1024;i++} printf "%0.2f %s", x, u[i+1] }'
          }

          if [ -f "$SUMMARY_FILE" ]; then
            TOTAL_CANDIDATES=$(grep -E '^TOTAL_CANDIDATES=' "$SUMMARY_FILE" | cut -d= -f2 || echo 0)
            TOTAL_CANDIDATES_BYTES=$(grep -E '^TOTAL_CANDIDATES_BYTES=' "$SUMMARY_FILE" | cut -d= -f2 || echo 0)
            TOTAL_DELETED=$(grep -E '^TOTAL_DELETED=' "$SUMMARY_FILE" | cut -d= -f2 || echo 0)
            TOTAL_DELETED_BYTES=$(grep -E '^TOTAL_DELETED_BYTES=' "$SUMMARY_FILE" | cut -d= -f2 || echo 0)

            {
              echo "## Docker Hub prune summary"
              echo "- candidates: ${TOTAL_CANDIDATES} (≈ $(human "${TOTAL_CANDIDATES_BYTES}"))"
              echo "- deleted: ${TOTAL_DELETED} (≈ $(human "${TOTAL_DELETED_BYTES}"))"
            } >> "$GITHUB_STEP_SUMMARY"
          else
            deleted_bytes=$(grep -oE '\( *approx +[0-9]+ bytes\)' "$LOG_FILE" | sed -E 's/.*approx +([0-9]+) bytes.*/\1/' | awk '{s+=$1} END {print s+0}' || true)
            deleted_count=$(grep -cE 'deleting |DRY RUN: would delete' "$LOG_FILE" || true)

            {
              echo "## Docker Hub prune summary"
              echo "- deleted (approx): ${deleted_count} (≈ $(human "${deleted_bytes}"))"
            } >> "$GITHUB_STEP_SUMMARY"
          fi

      - name: Upload Docker Hub prune artifacts
        if: always()
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
        with:
          name: prune-dockerhub-log-${{ github.run_id }}
          path: |
            prune-dockerhub-${{ github.run_id }}.log
            prune-summary-dockerhub.env

  summarize:
    runs-on: ubuntu-latest
    needs: [prune-ghcr, prune-dockerhub]
    if: always()
    steps:
      - name: Download all artifacts
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
        with:
          pattern: prune-*-log-${{ github.run_id }}
          merge-multiple: true

      - name: Combined summary
        run: |
          set -euo pipefail

          human() {
            local bytes=${1:-0}
            if [ -z "$bytes" ] || [ "$bytes" -eq 0 ]; then
              echo "0 B"
              return
            fi
            awk -v b="$bytes" 'BEGIN { split("B KiB MiB GiB TiB",u," "); i=0; x=b; while(x>1024){x/=1024;i++} printf "%0.2f %s", x, u[i+1] }'
          }

          GHCR_CANDIDATES=0 GHCR_CANDIDATES_BYTES=0 GHCR_DELETED=0 GHCR_DELETED_BYTES=0
          if [ -f prune-summary-ghcr.env ]; then
            GHCR_CANDIDATES=$(grep -E '^TOTAL_CANDIDATES=' prune-summary-ghcr.env | cut -d= -f2 || echo 0)
            GHCR_CANDIDATES_BYTES=$(grep -E '^TOTAL_CANDIDATES_BYTES=' prune-summary-ghcr.env | cut -d= -f2 || echo 0)
            GHCR_DELETED=$(grep -E '^TOTAL_DELETED=' prune-summary-ghcr.env | cut -d= -f2 || echo 0)
            GHCR_DELETED_BYTES=$(grep -E '^TOTAL_DELETED_BYTES=' prune-summary-ghcr.env | cut -d= -f2 || echo 0)
          fi

          HUB_CANDIDATES=0 HUB_CANDIDATES_BYTES=0 HUB_DELETED=0 HUB_DELETED_BYTES=0
          if [ -f prune-summary-dockerhub.env ]; then
            HUB_CANDIDATES=$(grep -E '^TOTAL_CANDIDATES=' prune-summary-dockerhub.env | cut -d= -f2 || echo 0)
            HUB_CANDIDATES_BYTES=$(grep -E '^TOTAL_CANDIDATES_BYTES=' prune-summary-dockerhub.env | cut -d= -f2 || echo 0)
            HUB_DELETED=$(grep -E '^TOTAL_DELETED=' prune-summary-dockerhub.env | cut -d= -f2 || echo 0)
            HUB_DELETED_BYTES=$(grep -E '^TOTAL_DELETED_BYTES=' prune-summary-dockerhub.env | cut -d= -f2 || echo 0)
          fi

          TOTAL_CANDIDATES=$((GHCR_CANDIDATES + HUB_CANDIDATES))
          TOTAL_CANDIDATES_BYTES=$((GHCR_CANDIDATES_BYTES + HUB_CANDIDATES_BYTES))
          TOTAL_DELETED=$((GHCR_DELETED + HUB_DELETED))
          TOTAL_DELETED_BYTES=$((GHCR_DELETED_BYTES + HUB_DELETED_BYTES))

          {
            echo "## Combined container prune summary"
            echo ""
            echo "| Registry | Candidates | Deleted | Space Reclaimed |"
            echo "|----------|------------|---------|-----------------|"
            echo "| GHCR | ${GHCR_CANDIDATES} | ${GHCR_DELETED} | $(human "${GHCR_DELETED_BYTES}") |"
            echo "| Docker Hub | ${HUB_CANDIDATES} | ${HUB_DELETED} | $(human "${HUB_DELETED_BYTES}") |"
            echo "| **Total** | **${TOTAL_CANDIDATES}** | **${TOTAL_DELETED}** | **$(human "${TOTAL_DELETED_BYTES}")** |"
          } >> "$GITHUB_STEP_SUMMARY"

          printf 'PRUNE_SUMMARY: candidates=%s candidates_bytes=%s deleted=%s deleted_bytes=%s\n' \
            "${TOTAL_CANDIDATES}" "${TOTAL_CANDIDATES_BYTES}" "${TOTAL_DELETED}" "${TOTAL_DELETED_BYTES}"
          echo "Total space reclaimed: $(human "${TOTAL_DELETED_BYTES}")"
