name: Docker Build, Push & Security Scan

on:
  push:
    branches: [main]
    tags: ['v*']
    paths:
      - 'Dockerfile'
      - '.dockerignore'
      - 'package.json'
      - 'bun.lock*'
      - '.github/workflows/docker-build.yml'
      - 'docker-entrypoint.sh'
      - 'drizzle/**'
      - 'scripts/**'
      - 'src/**'
  pull_request:
    paths:
      - 'Dockerfile'
      - '.dockerignore'
      - 'package.json'
      - 'bun.lock*'
      - '.github/workflows/docker-build.yml'
      - 'docker-entrypoint.sh'
      - 'drizzle/**'
      - 'scripts/**'
      - 'src/**'
  schedule:
    - cron: '0 0 * * 0'  # Weekly security scan on Sunday at midnight

env:
  REGISTRY: ghcr.io
  IMAGE: ${{ github.repository }}
  SHA: ${{ github.event.pull_request.head.sha || github.event.after }}

jobs:
  docker:
    runs-on: ubuntu-latest
    timeout-minutes: 25

    permissions:
      contents: write
      packages: write
      security-events: write
      pull-requests: write

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          ref: ${{ env.SHA }}

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          driver-opts: network=host

      - name: Log into registry
        if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      # Login to Docker Hub for Docker Scout (optional - provides better vulnerability data)
      # Add DOCKERHUB_USERNAME and DOCKERHUB_TOKEN secrets to enable this
      - name: Log into Docker Hub
        uses: docker/login-action@v3
        continue-on-error: true
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      # Extract version from tag if present
      - name: Extract version from tag
        id: tag_version
        run: |
          if [[ $GITHUB_REF == refs/tags/v* ]]; then
            TAG_VERSION="${GITHUB_REF#refs/tags/}"
            if [[ ! "$TAG_VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$ ]]; then
              echo "::error::Release tag '${TAG_VERSION}' is invalid. Expected semver tag format like v1.2.3 or v1.2.3-rc.1"
              exit 1
            fi
            APP_VERSION="${TAG_VERSION#v}"
            echo "VERSION=${TAG_VERSION}" >> $GITHUB_OUTPUT
            echo "APP_VERSION=${APP_VERSION}" >> $GITHUB_OUTPUT
            echo "Using version tag: ${TAG_VERSION}"
          else
            echo "VERSION=latest" >> $GITHUB_OUTPUT
            echo "APP_VERSION=dev" >> $GITHUB_OUTPUT
            echo "No version tag, using 'latest'"
          fi

      # Keep version files aligned automatically for tag-based releases
      - name: Sync app version from release tag
        if: startsWith(github.ref, 'refs/tags/v')
        run: |
          VERSION="${{ steps.tag_version.outputs.APP_VERSION }}"
          echo "Syncing package.json version to ${VERSION}"

          jq --arg version "${VERSION}" '.version = $version' package.json > package.json.tmp
          mv package.json.tmp package.json

          echo "Version sync diff (package.json):"
          git --no-pager diff -- package.json

      # Extract metadata for Docker
      - name: Extract Docker metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE }}
          labels: |
            org.opencontainers.image.revision=${{ env.SHA }}
          tags: |
            type=edge,branch=$repo.default_branch
            type=semver,pattern=v{{version}}
            type=sha,prefix=,suffix=,format=short
            type=raw,value=latest,enable={{is_default_branch}}
            type=raw,value=${{ steps.tag_version.outputs.VERSION }}
            type=ref,event=pr,prefix=pr-

      # Build and push Docker image
      - name: Build and push Docker image
        id: build-and-push
        uses: docker/build-push-action@v6
        with:
          context: .
          platforms: linux/amd64,linux/arm64
          push: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
          provenance: false # Disable provenance to avoid unknown/unknown
          sbom: false       # Disable sbom to avoid unknown/unknown

      # Load image locally for security scanning (PRs only)
      - name: Load image for scanning
        if: github.event_name == 'pull_request'
        uses: docker/build-push-action@v6
        with:
          context: .
          platforms: linux/amd64
          load: true
          tags: gitea-mirror:scan
          cache-from: type=gha
          provenance: false # Disable provenance to avoid unknown/unknown
          sbom: false       # Disable sbom to avoid unknown/unknown

      # Wait for image to be available in registry
      - name: Wait for image availability
        if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
        run: |
          echo "Waiting for image to be available in registry..."
          sleep 5

      # Add comment to PR with image details
      - name: Comment PR with image tag
        if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
        uses: actions/github-script@v7
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            const prNumber = context.payload.pull_request.number;
            const imageTag = `pr-${prNumber}`;
            const imagePath = `${{ env.REGISTRY }}/${{ env.IMAGE }}:${imageTag}`.toLowerCase();

            const comment = `## 🐳 Docker Image Built Successfully

            Your PR image is available for testing:

            **Image Tag:** \`${imageTag}\`
            **Full Image Path:** \`${imagePath}\`

            ### Pull and Test
            \`\`\`bash
            docker pull ${imagePath}
            docker run -d \
              -p 4321:4321 \
              -e BETTER_AUTH_SECRET=your-secret-here \
              -e BETTER_AUTH_URL=http://localhost:4321 \
              --name gitea-mirror-test ${imagePath}
            \`\`\`

            ### Docker Compose Testing
            \`\`\`yaml
            services:
              gitea-mirror:
                image: ${imagePath}
                ports:
                  - "4321:4321"
                environment:
                  - BETTER_AUTH_SECRET=your-secret-here
                  - BETTER_AUTH_URL=http://localhost:4321
                  - BETTER_AUTH_TRUSTED_ORIGINS=http://localhost:4321
            \`\`\`

            > 💡 **Note:** PR images are tagged as \`pr-<number>\` and built for both \`linux/amd64\` and \`linux/arm64\`.
            > Production images (\`latest\`, version tags) use the same multi-platform set.

            ---
            📦 View in [GitHub Packages](https://github.com/${{ github.repository }}/pkgs/container/gitea-mirror)`;

            github.rest.issues.createComment({
              issue_number: prNumber,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: comment
            });

      # Docker Scout comprehensive security analysis
      - name: Docker Scout - Vulnerability Analysis & Recommendations
        uses: docker/scout-action@v1
        if: github.event_name != 'pull_request'
        with:
          command: cves,recommendations
          image: ${{ env.REGISTRY }}/${{ env.IMAGE }}:latest
          sarif-file: scout-results.sarif
          summary: true
          exit-code: false
          only-severities: critical,high
          write-comment: true
          github-token: ${{ secrets.GITHUB_TOKEN }}

      # Docker Scout for Pull Requests (using local image)
      - name: Docker Scout - Vulnerability Analysis (PR)
        uses: docker/scout-action@v1
        if: github.event_name == 'pull_request'
        with:
          command: cves,recommendations
          image: local://gitea-mirror:scan
          sarif-file: scout-results.sarif
          summary: true
          exit-code: false
          only-severities: critical,high
          write-comment: true
          github-token: ${{ secrets.GITHUB_TOKEN }}

      # Compare to latest for PRs and pushes
      - name: Docker Scout - Compare to Latest
        uses: docker/scout-action@v1
        if: github.event_name == 'pull_request'
        with:
          command: compare
          image: local://gitea-mirror:scan
          to: ${{ env.REGISTRY }}/${{ env.IMAGE }}:latest
          ignore-unchanged: true
          only-severities: critical,high
          write-comment: true
          github-token: ${{ secrets.GITHUB_TOKEN }}

      # Upload security scan results to GitHub Security tab
      - name: Upload Docker Scout scan results to GitHub Security tab
        uses: github/codeql-action/upload-sarif@v4
        if: always()
        continue-on-error: true
        with:
          sarif_file: scout-results.sarif

  sync-version-main:
    name: Sync package.json version back to main
    if: startsWith(github.ref, 'refs/tags/v')
    runs-on: ubuntu-latest
    needs: docker
    permissions:
      contents: write

    steps:
      - name: Checkout default branch
        uses: actions/checkout@v4
        with:
          ref: ${{ github.event.repository.default_branch }}

      - name: Update package.json version on main
        env:
          TAG_VERSION: ${{ github.ref_name }}
          TARGET_BRANCH: ${{ github.event.repository.default_branch }}
        run: |
          if [[ ! "$TAG_VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$ ]]; then
            echo "::error::Release tag '${TAG_VERSION}' is invalid. Expected semver tag format like v1.2.3 or v1.2.3-rc.1"
            exit 1
          fi

          APP_VERSION="${TAG_VERSION#v}"
          echo "Syncing ${TARGET_BRANCH}/package.json to ${APP_VERSION}"

          jq --arg version "${APP_VERSION}" '.version = $version' package.json > package.json.tmp
          mv package.json.tmp package.json

          if git diff --quiet -- package.json; then
            echo "package.json on ${TARGET_BRANCH} already at ${APP_VERSION}; nothing to commit."
            exit 0
          fi

          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
          git add package.json
          git commit -m "chore: sync version to ${APP_VERSION}"
          git push origin "HEAD:${TARGET_BRANCH}"
