name: Publish to DockerHub
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
tag:
description: 'Tag to publish (e.g., v1.3.0, dev-build, test, latest)'
required: true
default: 'dev-build'
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
security-events: write # Required for SARIF upload
id-token: write # Required for attestation
steps:
- uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: aywengo/kafka-schema-reg-mcp
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=ref,event=tag
type=raw,value=latest,enable={{is_default_branch}}
type=raw,value=${{ github.event.inputs.tag }},enable=${{ github.event_name == 'workflow_dispatch' }}
labels: |
org.opencontainers.image.title=Kafka Schema Registry MCP Server
org.opencontainers.image.description=True MCP server for Kafka Schema Registry with OAuth integration (Azure/Google/Keycloak/Okta), 70+ tools, multi-registry support, Kubernetes deployment, and Claude Desktop integration. Use SLIM_MODE=true to reduce tools from 70+ to ~20.
org.opencontainers.image.vendor=aywengo
org.opencontainers.image.version={{version}}
org.opencontainers.image.created={{date 'YYYY-MM-DDTHH:mm:ssZ'}}
org.opencontainers.image.revision={{sha}}
org.opencontainers.image.url=https://github.com/${{ github.repository }}
org.opencontainers.image.source=https://github.com/${{ github.repository }}
org.opencontainers.image.documentation=https://github.com/${{ github.repository }}#readme
- name: Build and push multi-platform
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: false # Disable provenance for multi-platform compatibility
build-args: |
VERSION=${{ steps.meta.outputs.version }}
BUILD_DATE=${{ steps.meta.outputs.created }}
VCS_REF=${{ github.sha }}
- name: Verify multi-platform build
run: |
echo "Verifying multi-platform manifest..."
docker buildx imagetools inspect aywengo/kafka-schema-reg-mcp:${{ steps.meta.outputs.version }} > manifest.txt
cat manifest.txt
echo "Checking available platforms..."
# Verify both platforms are present in the buildx inspect output
if grep -q "linux/amd64" manifest.txt && grep -q "linux/arm64" manifest.txt; then
echo "✅ Multi-platform build verified: AMD64 and ARM64 both available"
echo "Available platforms:"
grep -E "Platform:\s+linux/(amd64|arm64)" manifest.txt
else
echo "❌ Multi-platform build failed: Missing platforms"
echo "Found platforms:"
grep -E "Platform:\s+" manifest.txt || echo "No platforms found"
exit 1
fi
- name: Wait for Docker Hub to sync image
run: sleep 120
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: 'aywengo/kafka-schema-reg-mcp:${{ steps.meta.outputs.version }}'
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
timeout: '10m0s'
continue-on-error: true # Don't fail the workflow if Trivy scan fails
- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v4
if: always()
continue-on-error: true # Don't fail the workflow if SARIF upload fails
with:
sarif_file: 'trivy-results.sarif'
- name: Generate Trivy table report for published image
if: always()
run: |
echo "## Published Image Security Scan" >> $GITHUB_STEP_SUMMARY
echo "- 🎯 **Severity Filter**: CRITICAL and HIGH only" >> $GITHUB_STEP_SUMMARY
echo "- ❌ **Excluded**: MEDIUM and LOW severity issues" >> $GITHUB_STEP_SUMMARY
echo "- 📦 **Image**: aywengo/kafka-schema-reg-mcp:${{ steps.meta.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "- 🔧 **Tools**: 70+ tools (full feature set) - Use SLIM_MODE=true to reduce to ~20 tools" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
# Generate table format scan for summary
docker run --rm aquasec/trivy:latest image \
--format table \
--severity CRITICAL,HIGH \
--timeout 10m \
aywengo/kafka-schema-reg-mcp:${{ steps.meta.outputs.version }} | tee trivy-table.txt || true
if [ -f "trivy-results.sarif" ]; then
echo "- ✅ Security scan completed and uploaded to GitHub Security tab" >> $GITHUB_STEP_SUMMARY
else
echo "- ⚠️ Security scan results not available" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Note**: This published image follows the security policy of reporting only CRITICAL and HIGH severity vulnerabilities." >> $GITHUB_STEP_SUMMARY
- name: Update Docker Hub description
id: dockerhub-description
uses: peter-evans/dockerhub-description@v5
continue-on-error: true # Don't fail the workflow if Docker Hub update fails
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
repository: aywengo/kafka-schema-reg-mcp
short-description: "MCP Server for Kafka Schema Registry - 70+ tools, OAuth, multi-registry, Kubernetes ready"
readme-filepath: ./dockerhub-readme.md
- name: Docker Hub description update status
if: always()
run: |
if [ "${{ steps.dockerhub-description.outcome }}" == "success" ]; then
echo "✅ Docker Hub description updated successfully"
else
echo "⚠️ Docker Hub description update failed - this is non-critical"
echo "The Docker image was published successfully but the description update failed"
echo "This can happen due to Docker Hub API rate limits or temporary issues"
fi
create-release:
runs-on: ubuntu-latest
needs: publish
if: startsWith(github.ref, 'refs/tags/v')
permissions:
contents: write # Required to create releases
steps:
- uses: actions/checkout@v6
- name: Extract changelog section
id: changelog
run: |
# Convert tag (v2.0.2) to changelog format ([2.0.2])
VERSION=${GITHUB_REF_NAME#v} # Remove 'v' prefix
CHANGELOG_SECTION="[$VERSION]"
echo "Looking for changelog section: $CHANGELOG_SECTION"
# Extract the section from CHANGELOG.md
awk '/^## \['"$VERSION"'\]/ { found=1; print; next }
/^## \[/ && found { found=0 }
found { print }' CHANGELOG.md > release_notes.md
# Check if we found content
if [ ! -s release_notes.md ]; then
echo "Warning: No changelog section found for version $VERSION"
echo "## Release $GITHUB_REF_NAME" > release_notes.md
echo "" >> release_notes.md
echo "Please see [CHANGELOG.md](CHANGELOG.md) for detailed release notes." >> release_notes.md
else
# Add a header to the extracted content
{
echo "## Release $GITHUB_REF_NAME"
echo ""
cat release_notes.md
echo ""
echo "---"
echo ""
echo "For complete version history, see [CHANGELOG.md](CHANGELOG.md)."
} > temp_notes.md
mv temp_notes.md release_notes.md
fi
# Set the output for the next step
echo "changelog_content<<EOF" >> $GITHUB_OUTPUT
cat release_notes.md >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref_name }}
name: Release ${{ github.ref_name }}
body: ${{ steps.changelog.outputs.changelog_content }}
draft: false
prerelease: false