name: 📦 Semantic Release

on:
  push:
    branches:
      - master
      - main
  workflow_dispatch:

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

# Least privilege by default; jobs opt into more only where they need it.
permissions:
  contents: read

# Never cancel an in-flight release; queue instead.
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: false

jobs:
  test:
    name: 🧪 Test Before Release
    runs-on: ubuntu-latest
    timeout-minutes: 20
    permissions:
      contents: read
    steps:
      - name: 📥 Checkout
        uses: actions/checkout@v7
        with:
          persist-credentials: false

      - name: 📦 Setup Node.js
        uses: actions/setup-node@v7
        with:
          node-version: '22'
          cache: 'npm'

      - name: 📥 Install dependencies
        run: npm ci

      - name: 🔍 Lint
        run: npm run lint

      - name: 🏗️ Build
        run: npm run build

      - name: 🔬 Type check
        run: npm run typecheck

      - name: 🧪 Test
        run: npm test

  security-scan:
    name: 🔍 Security Scan
    runs-on: ubuntu-latest
    timeout-minutes: 20
    permissions:
      contents: read
      security-events: write

    steps:
      - name: 📥 Checkout
        uses: actions/checkout@v7
        with:
          persist-credentials: false

      - name: 🔒 Run Trivy vulnerability scanner
        # Pinned to the v0.36.0 tag commit — @master is a mutable ref.
        uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
        with:
          scan-type: 'fs'
          scan-ref: '.'
          format: 'sarif'
          output: 'trivy-results.sarif'

      - name: 📤 Upload Trivy scan results to GitHub Security
        uses: github/codeql-action/upload-sarif@v4
        if: always()
        with:
          sarif_file: 'trivy-results.sarif'
          category: 'trivy-filesystem'

      - name: 🐳 Build Docker image for security scan
        run: |
          docker build -t security-scan-image .

      - name: 🔒 Run Trivy on Docker image
        # Pinned to the v0.36.0 tag commit — @master is a mutable ref.
        uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
        with:
          image-ref: 'security-scan-image'
          format: 'sarif'
          output: 'trivy-docker-results.sarif'

      - name: 📤 Upload Docker scan results
        uses: github/codeql-action/upload-sarif@v4
        if: always()
        with:
          sarif_file: 'trivy-docker-results.sarif'
          category: 'trivy-docker'

  dependency-check:
    name: 📦 Dependency Check
    runs-on: ubuntu-latest
    timeout-minutes: 15
    permissions:
      contents: read

    steps:
      - name: 📥 Checkout
        uses: actions/checkout@v7
        with:
          persist-credentials: false

      - name: 📦 Setup Node.js
        uses: actions/setup-node@v7
        with:
          node-version: '22'
          cache: 'npm'

      - name: 📥 Install dependencies
        run: npm ci

      - name: 🔍 Audit dependencies
        # Using audit-ci for allowlisting, npm audit for basic check (non-blocking)
        run: npm audit --audit-level=moderate || true

      - name: 📊 Check for outdated packages
        run: npm outdated || true

      - name: 🔒 Check for known vulnerabilities
        # audit-ci respects the allowlist in audit-ci.json — advisories with no
        # reachable fix (bundled/unpatched transitive dev deps). Each entry carries
        # a rationale + re-check trigger in $allowlist-rationale.
        # Version-pinned so the gate itself cannot shift under us.
        run: npx --yes audit-ci@7.1.0 --config audit-ci.json

  dockerfile-lint:
    name: 🐳 Dockerfile Lint
    runs-on: ubuntu-latest
    timeout-minutes: 10
    permissions:
      contents: read
      security-events: write

    steps:
      - name: 📥 Checkout
        uses: actions/checkout@v7
        with:
          persist-credentials: false

      - name: 🔍 Lint Dockerfile
        uses: hadolint/hadolint-action@v3.1.0
        with:
          dockerfile: Dockerfile
          format: sarif
          output-file: hadolint-results.sarif
          no-fail: true

      - name: 📤 Upload Dockerfile lint results
        uses: github/codeql-action/upload-sarif@v4
        if: always()
        with:
          sarif_file: hadolint-results.sarif
          category: 'hadolint'

  release:
    name: 📦 Release
    needs: [test, security-scan, dependency-check, dockerfile-lint]
    runs-on: ubuntu-latest
    timeout-minutes: 30
    permissions:
      contents: write
      issues: write
      pull-requests: write
      id-token: write
    outputs:
      release-version: ${{ steps.get-version.outputs.version }}
      new-release-published: ${{ steps.check-release.outputs.released }}

    steps:
      - name: 📥 Checkout
        uses: actions/checkout@v7
        with:
          fetch-depth: 0
          token: ${{ secrets.GITHUB_TOKEN }}

      - name: 📦 Setup Node.js
        uses: actions/setup-node@v7
        with:
          node-version: '22'
          cache: 'npm'
          registry-url: 'https://registry.npmjs.org'

      - name: 📥 Install dependencies
        run: npm ci

      - name: 🏗️ Build
        run: npm run build

      - name: 🏷️ Get latest tag before release
        id: tag-before
        run: |
          latest_tag=$(git describe --tags --abbrev=0 2>/dev/null || echo "none")
          echo "tag=$latest_tag" >> $GITHUB_OUTPUT
          echo "Latest tag before release: $latest_tag"

      - name: 📦 Run semantic-release
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: npx semantic-release

      - name: 🏷️ Get latest tag after release
        id: tag-after
        run: |
          git fetch --tags
          latest_tag=$(git describe --tags --abbrev=0 2>/dev/null || echo "none")
          echo "tag=$latest_tag" >> $GITHUB_OUTPUT
          echo "Latest tag after release: $latest_tag"

      - name: 📦 Extract version from new tag
        id: get-version
        run: |
          if [ "${{ steps.tag-after.outputs.tag }}" != "none" ]; then
            version=$(echo "${{ steps.tag-after.outputs.tag }}" | sed 's/^v//')
            echo "version=$version" >> $GITHUB_OUTPUT
            echo "Extracted version: $version"
          else
            echo "version=" >> $GITHUB_OUTPUT
          fi

      - name: 🔍 Check if new release was published
        id: check-release
        run: |
          if [ "${{ steps.tag-before.outputs.tag }}" != "${{ steps.tag-after.outputs.tag }}" ] && [ "${{ steps.tag-after.outputs.tag }}" != "none" ]; then
            echo "released=true" >> $GITHUB_OUTPUT
            echo "New release detected: ${{ steps.tag-after.outputs.tag }}"
          else
            echo "released=false" >> $GITHUB_OUTPUT
            echo "No new release"
          fi

      - name: 📦 Upgrade npm for OIDC publish support
        if: steps.check-release.outputs.released == 'true'
        run: |
          npx npm@11 --version
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: 📤 Publish to npm with provenance
        if: steps.check-release.outputs.released == 'true'
        run: npx npm@11 publish --provenance --access public

      - name: 📊 Release Summary
        if: success()
        run: |
          echo "🎉 **Semantic Release Completed Successfully!**" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "📋 **What happened:**" >> $GITHUB_STEP_SUMMARY
          echo "- 🔍 Analyzed commits since last release" >> $GITHUB_STEP_SUMMARY
          echo "- 📝 Generated changelog entries" >> $GITHUB_STEP_SUMMARY
          echo "- 🏷️ Created git tag (if new version)" >> $GITHUB_STEP_SUMMARY
          echo "- 📦 Updated package.json version" >> $GITHUB_STEP_SUMMARY
          if [ "${{ steps.check-release.outputs.released }}" == "true" ]; then
            echo "- ✅ **New version released:** ${{ steps.get-version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
            echo "- 🐳 Docker images will be built next" >> $GITHUB_STEP_SUMMARY
          else
            echo "- ℹ️ **No new version:** No changes warranted a release" >> $GITHUB_STEP_SUMMARY
          fi

  docker:
    name: 🐳 Build & Push Docker Images
    needs: release
    runs-on: ubuntu-latest
    timeout-minutes: 90
    permissions:
      contents: read
      packages: write
    if: needs.release.outputs.new-release-published == 'true'

    steps:
      - name: 📥 Checkout release tag
        uses: actions/checkout@v7
        with:
          ref: v${{ needs.release.outputs.release-version }}
          fetch-depth: 0

      - name: 🛠️ Set up QEMU
        uses: docker/setup-qemu-action@v4

      - name: 🛠️ Set up Docker Buildx
        uses: docker/setup-buildx-action@v4

      - name: 🔑 Login to Container Registry
        uses: docker/login-action@v4
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: 🏷️ Extract metadata
        id: meta
        uses: docker/metadata-action@v6
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=semver,pattern={{version}},value=${{ needs.release.outputs.release-version }}
            type=semver,pattern={{major}}.{{minor}},value=${{ needs.release.outputs.release-version }}
            type=semver,pattern={{major}},value=${{ needs.release.outputs.release-version }}
            type=raw,value=latest
          labels: |
            org.opencontainers.image.title=MCP Rubber Duck
            org.opencontainers.image.description=Multi-platform MCP server for multiple OpenAI-compatible LLMs
            org.opencontainers.image.version=${{ needs.release.outputs.release-version }}

      - name: 🐳 Build and push Docker image
        id: build
        uses: docker/build-push-action@v7
        with:
          context: .
          platforms: linux/amd64,linux/arm64
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
          build-args: |
            BUILDKIT_INLINE_CACHE=1

      - name: 🔧 Make package public
        if: success()
        run: |
          # Wait a moment for package to appear
          sleep 10

          # Try to make package public (may fail if already public)
          gh api --method PATCH \
            "user/packages/container/${{ github.event.repository.name }}" \
            --field visibility=public \
            --silent || echo "Package may already be public or visibility change failed"
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: 📊 Docker Success Summary
        if: success()
        run: |
          echo "🎉 **Docker Images Built Successfully!**" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "📦 **Built for version:** ${{ needs.release.outputs.release-version }}" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "🐳 **Docker Images Published:**" >> $GITHUB_STEP_SUMMARY
          for tag in $(echo '${{ steps.meta.outputs.tags }}' | tr ',' '\n'); do
            echo "- \`$tag\`" >> $GITHUB_STEP_SUMMARY
          done
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "🏗️ **Platforms:** AMD64, ARM64" >> $GITHUB_STEP_SUMMARY
          echo "📍 **Registry:** ${{ env.REGISTRY }}" >> $GITHUB_STEP_SUMMARY
          echo "🔗 **Package URL:** https://github.com/${{ github.repository }}/pkgs/container/${{ github.event.repository.name }}" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "🚀 **Quick Test:**" >> $GITHUB_STEP_SUMMARY
          echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY
          echo "docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.release.outputs.release-version }}" >> $GITHUB_STEP_SUMMARY
          echo "docker run --rm -e OPENAI_API_KEY=your-key ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.release.outputs.release-version }}" >> $GITHUB_STEP_SUMMARY
          echo "\`\`\`" >> $GITHUB_STEP_SUMMARY