name: Release
# Add permissions for release creation
permissions:
contents: write # Required for creating releases and tags
packages: write # Required for uploading release assets
on:
push:
branches:
- master
workflow_dispatch:
inputs:
version_bump:
description: 'Version bump type (patch, minor, major)'
required: true
type: choice
options:
- patch
- minor
- major
default: patch
custom_changelog:
description: 'Custom changelog (optional)'
required: false
type: string
default: ''
jobs:
release:
name: Create Release
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install build twine
- name: Build package
run: |
python -m build
- name: Check package
run: |
twine check dist/*
- name: Get current version and generate new tag
id: version
run: |
# Get the latest tag
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
echo "Current latest tag: $LATEST_TAG"
# Extract version number (remove 'v' prefix)
VERSION=${LATEST_TAG#v}
echo "Current version: $VERSION"
# Split version into parts
IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION"
# Determine version bump type
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
BUMP_TYPE="${{ github.event.inputs.version_bump }}"
else
# Auto-detect bump type based on commit messages
COMMITS=$(git log --pretty=format:"%s" --no-merges HEAD~10..HEAD)
if echo "$COMMITS" | grep -qiE "(breaking|major|!:)"; then
BUMP_TYPE="major"
elif echo "$COMMITS" | grep -qiE "(feat|feature|minor)"; then
BUMP_TYPE="minor"
else
BUMP_TYPE="patch"
fi
fi
echo "Bump type: $BUMP_TYPE"
# Calculate new version
case $BUMP_TYPE in
major)
NEW_MAJOR=$((MAJOR + 1))
NEW_MINOR=0
NEW_PATCH=0
;;
minor)
NEW_MAJOR=$MAJOR
NEW_MINOR=$((MINOR + 1))
NEW_PATCH=0
;;
patch)
NEW_MAJOR=$MAJOR
NEW_MINOR=$MINOR
NEW_PATCH=$((PATCH + 1))
;;
esac
NEW_VERSION="$NEW_MAJOR.$NEW_MINOR.$NEW_PATCH"
NEW_TAG="v$NEW_VERSION"
echo "New version: $NEW_VERSION"
echo "New tag: $NEW_TAG"
# Output for next steps
echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT
echo "tag=$NEW_TAG" >> $GITHUB_OUTPUT
echo "release_name=Release $NEW_TAG" >> $GITHUB_OUTPUT
echo "bump_type=$BUMP_TYPE" >> $GITHUB_OUTPUT
- name: Generate changelog
id: changelog
run: |
# Handle custom changelog for manual releases
if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ -n "${{ github.event.inputs.custom_changelog }}" ]; then
echo "changelog<<EOF" >> $GITHUB_OUTPUT
echo "${{ github.event.inputs.custom_changelog }}" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
else
# Generate automatic changelog
LAST_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "v0.0.0")
echo "changelog<<EOF" >> $GITHUB_OUTPUT
echo "## What's Changed" >> $GITHUB_OUTPUT
echo "" >> $GITHUB_OUTPUT
# Get commits since last tag
if [ "$LAST_TAG" != "v0.0.0" ]; then
COMMITS=$(git log --pretty=format:"- %s" --no-merges ${LAST_TAG}..HEAD)
echo "$COMMITS" >> $GITHUB_OUTPUT
else
# First release - get all commits
COMMITS=$(git log --pretty=format:"- %s" --no-merges)
echo "$COMMITS" >> $GITHUB_OUTPUT
fi
echo "" >> $GITHUB_OUTPUT
echo "**Version Bump**: ${{ steps.version.outputs.bump_type }}" >> $GITHUB_OUTPUT
echo "**Full Changelog**: https://github.com/${{ github.repository }}/compare/${LAST_TAG}...${{ steps.version.outputs.tag }}" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
fi
- name: Create and push tag
run: |
# Check if tag already exists
if git rev-parse "${{ steps.version.outputs.tag }}" >/dev/null 2>&1; then
echo "Tag ${{ steps.version.outputs.tag }} already exists"
else
echo "Creating and pushing tag ${{ steps.version.outputs.tag }}"
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git tag ${{ steps.version.outputs.tag }}
git push origin ${{ steps.version.outputs.tag }}
fi
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.version.outputs.tag }}
release_name: ${{ steps.version.outputs.release_name }}
body: ${{ steps.changelog.outputs.changelog }}
draft: false
prerelease: false
- name: List built files
run: |
echo "Built files in dist/:"
ls -la ./dist/
# Find the tar.gz file
TAR_FILE=$(find ./dist/ -name "*.tar.gz" | head -1)
if [ -z "$TAR_FILE" ]; then
echo "No tar.gz file found in dist/"
exit 1
fi
echo "Found tar.gz file: $TAR_FILE"
echo "TAR_FILE=$TAR_FILE" >> $GITHUB_ENV
- name: Upload Release Assets
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ${{ env.TAR_FILE }}
asset_name: zap-custom-mcp-${{ steps.version.outputs.version }}-dist.tar.gz
asset_content_type: application/gzip
docker-release:
name: Build and Push Docker Image
runs-on: ubuntu-latest
needs: release
if: false # Disabled - no Docker release
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: zap-custom-mcp
tags: |
type=ref,event=tag
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Docker image
uses: docker/build-push-action@v5
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
notify:
name: Notify Release
runs-on: ubuntu-latest
needs: [release] # Only depends on release job
if: always()
steps:
- name: Notify success
if: needs.release.result == 'success'
run: |
echo "✅ Release ${{ github.ref_name }} created successfully!"
echo "📦 Python package built and uploaded"
- name: Notify failure
if: needs.release.result == 'failure'
run: |
echo "❌ Release failed!"
echo "Release job: ${{ needs.release.result }}"