name: Docker Release

# Only trigger on GitHub release publication, not on tag push.
# When a release is created, GitHub fires both 'release' and 'push' events.
# Using only 'release' prevents duplicate workflow runs.
on:
  release:
    types: [published]

# Cancel in-progress runs for the same tag
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

env:
  DOCKERHUB_REPO: spkane/freecad-robust-mcp

jobs:
  release:
    name: Build and Push to Docker Hub
    runs-on: ubuntu-latest
    permissions:
      contents: read

    steps:
      - name: Checkout code
        uses: actions/checkout@v6

      - name: Validate semantic version tag
        id: version
        run: |
          TAG="${GITHUB_REF#refs/tags/}"
          echo "tag=$TAG" >> "$GITHUB_OUTPUT"

          # Validate semantic versioning format (v1.2.3 or v1.2.3-prerelease)
          if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then
              echo "ERROR: Tag '$TAG' does not follow semantic versioning (vX.Y.Z or vX.Y.Z-prerelease)"
              exit 1
          fi

          # Extract version without 'v' prefix
          VERSION="${TAG#v}"
          echo "version=$VERSION" >> "$GITHUB_OUTPUT"

          # Extract major.minor for additional tag
          MAJOR_MINOR=$(echo "$VERSION" | cut -d. -f1,2)
          echo "major_minor=$MAJOR_MINOR" >> "$GITHUB_OUTPUT"

          # Extract major for additional tag
          MAJOR=$(echo "$VERSION" | cut -d. -f1)
          echo "major=$MAJOR" >> "$GITHUB_OUTPUT"

          # Check if this is a prerelease
          if [[ "$TAG" =~ -[a-zA-Z0-9.]+ ]]; then
              echo "is_prerelease=true" >> "$GITHUB_OUTPUT"
              IS_PRERELEASE="true"
          else
              echo "is_prerelease=false" >> "$GITHUB_OUTPUT"
              IS_PRERELEASE="false"
          fi

          echo "Parsed version: $VERSION (major: $MAJOR, major.minor: $MAJOR_MINOR, prerelease: $IS_PRERELEASE)"

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Log in to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ vars.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Extract metadata for Docker
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.DOCKERHUB_REPO }}
          tags: |
            # Full semantic version (v1.2.3 -> 1.2.3)
            type=semver,pattern={{version}}
            # Major.minor version (v1.2.3 -> 1.2)
            type=semver,pattern={{major}}.{{minor}}
            # Major version only (v1.2.3 -> 1) - only for stable releases
            type=semver,pattern={{major}},enable=${{ steps.version.outputs.is_prerelease == 'false' }}
            # Latest tag - only for stable releases
            type=raw,value=latest,enable=${{ steps.version.outputs.is_prerelease == 'false' }}
          labels: |
            org.opencontainers.image.title=FreeCAD Robust MCP Server
            org.opencontainers.image.description=Model Context Protocol server for FreeCAD integration
            org.opencontainers.image.vendor=spkane
            org.opencontainers.image.version=${{ steps.version.outputs.version }}

      - name: Build and push Docker image
        uses: docker/build-push-action@v6
        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
          provenance: true
          sbom: true

      - name: Test pushed image
        run: |
          # Pull and test the image
          docker pull ${{ env.DOCKERHUB_REPO }}:${{ steps.version.outputs.version }}

          # Test that the container starts and responds to MCP initialize
          echo '{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' | \
            timeout 30 docker run --rm -i \
              -e FREECAD_MODE=xmlrpc \
              ${{ env.DOCKERHUB_REPO }}:${{ steps.version.outputs.version }} 2>&1 | \
            grep -q '"result"' && echo "Container test passed" || echo "Container test completed"

      - name: Create Docker Hub README
        run: |
          # Create a concise README for Docker Hub (limit is 25000 bytes)
          cat > ./DOCKERHUB_README.md << 'EOF'
          # FreeCAD Robust MCP Server

          An [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) server that enables integration between AI assistants (Claude, GPT, and other MCP-compatible tools) and [FreeCAD](https://www.freecadweb.org/), allowing AI-assisted development and debugging of 3D models, macros, and workbenches.

          ## Features

          - **82+ MCP Tools**: Comprehensive CAD operations including primitives, PartDesign, booleans, export
          - **Multiple Connection Modes**: XML-RPC (recommended), JSON-RPC socket, or embedded
          - **GUI & Headless Support**: Full modeling in headless mode, plus screenshots/colors in GUI mode
          - **Macro Development**: Create, edit, run, and template FreeCAD macros via MCP

          ## Quick Start

          ```bash
          docker pull spkane/freecad-robust-mcp:latest
          ```

          ### Running with Claude Desktop (macOS/Windows)

          Add to your Claude Desktop configuration:

          ```json
          {
            "mcpServers": {
              "freecad": {
                "command": "docker",
                "args": [
                  "run", "-i", "--rm",
                  "-e", "FREECAD_MODE=xmlrpc",
                  "-e", "FREECAD_SOCKET_HOST=host.docker.internal",
                  "spkane/freecad-robust-mcp:latest"
                ]
              }
            }
          }
          ```

          ### Linux Users

          `host.docker.internal` is only available on Docker Desktop (macOS/Windows).
          On Linux Docker Engine, use one of these alternatives:

          **Option 1: Host networking (recommended)**
          ```bash
          docker run -i --rm --network=host \
            -e FREECAD_MODE=xmlrpc \
            -e FREECAD_SOCKET_HOST=localhost \
            spkane/freecad-robust-mcp:latest
          ```

          **Option 2: Use host IP address**
          ```bash
          docker run -i --rm \
            -e FREECAD_MODE=xmlrpc \
            -e FREECAD_SOCKET_HOST=192.168.1.100 \
            spkane/freecad-robust-mcp:latest
          ```

          **Option 3: Add host gateway (Docker 20.10+)**
          ```bash
          docker run -i --rm --add-host=host.docker.internal:host-gateway \
            -e FREECAD_MODE=xmlrpc \
            -e FREECAD_SOCKET_HOST=host.docker.internal \
            spkane/freecad-robust-mcp:latest
          ```

          ## Connection Modes

          | Mode | Description | Use Case |
          |------|-------------|----------|
          | `xmlrpc` | XML-RPC over HTTP (port 9875) | **Recommended** - Works with FreeCAD GUI or headless |
          | `socket` | JSON-RPC over TCP (port 9876) | Alternative to XML-RPC |
          | `embedded` | Direct Python import | Linux only, requires FreeCAD in Python path |

          ## Environment Variables

          | Variable | Default | Description |
          |----------|---------|-------------|
          | `FREECAD_MODE` | `xmlrpc` | Connection mode |
          | `FREECAD_SOCKET_HOST` | `localhost` | Server host (used for both XML-RPC and socket modes) |
          | `FREECAD_XMLRPC_PORT` | `9875` | XML-RPC server port |
          | `FREECAD_SOCKET_PORT` | `9876` | Socket server port |

          ## Starting FreeCAD with MCP Bridge

          Before using this Docker image, start FreeCAD with the MCP bridge:

          1. Open FreeCAD
          2. Go to **Macro → Macros → StartMCPBridge** (or run from Python console)
          3. The bridge will start listening on ports 9875 (XML-RPC) and 9876 (Socket)

          ## Full Documentation

          📖 **For complete documentation, examples, and source code, visit:**

          **[https://github.com/spkane/freecad-robust-mcp-and-more](https://github.com/spkane/freecad-robust-mcp-and-more)**

          ## License

          MIT License - see [LICENSE](https://github.com/spkane/freecad-robust-mcp-and-more/blob/main/LICENSE) for details.
          EOF

          # Validate the generated README
          if [ ! -f ./DOCKERHUB_README.md ]; then
              echo "ERROR: DOCKERHUB_README.md was not created" >&2
              exit 1
          fi

          if [ ! -s ./DOCKERHUB_README.md ]; then
              echo "ERROR: DOCKERHUB_README.md is empty" >&2
              exit 1
          fi

          # Check file size (Docker Hub limit is 25000 bytes)
          FILE_SIZE=$(wc -c < ./DOCKERHUB_README.md)
          MAX_SIZE=25000
          echo "Docker Hub README size: ${FILE_SIZE} bytes (limit: ${MAX_SIZE} bytes)"

          if [ "$FILE_SIZE" -gt "$MAX_SIZE" ]; then
              echo "ERROR: DOCKERHUB_README.md exceeds Docker Hub limit of ${MAX_SIZE} bytes (actual: ${FILE_SIZE} bytes)" >&2
              exit 1
          fi

          echo "Docker Hub README validation passed"

      - name: Update Docker Hub description
        uses: peter-evans/dockerhub-description@v5
        with:
          username: ${{ vars.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}
          repository: ${{ env.DOCKERHUB_REPO }}
          readme-filepath: ./DOCKERHUB_README.md
          short-description: "MCP (Model Context Protocol) server for FreeCAD integration"
        continue-on-error: true

      - name: Generate release summary
        run: |
          {
            echo "## Docker Release Summary"
            echo ""
            echo "**Version:** ${{ steps.version.outputs.version }}"
            echo "**Prerelease:** ${{ steps.version.outputs.is_prerelease }}"
            echo ""
            echo "### Published Tags"
            echo ""
            echo "\`\`\`"
            echo "${{ steps.meta.outputs.tags }}"
            echo "\`\`\`"
            echo ""
            echo "### Pull Command"
            echo ""
            echo "\`\`\`bash"
            echo "docker pull ${{ env.DOCKERHUB_REPO }}:${{ steps.version.outputs.version }}"
            echo "\`\`\`"
          } >> "$GITHUB_STEP_SUMMARY"
