Merge pull request #217 from svnlto/main

feat: streamline GitHub Actions workflows and improve contributor experience
This commit is contained in:
Beehive Innovations
2025-08-20 08:36:21 +04:00
committed by GitHub
13 changed files with 503 additions and 434 deletions

View File

@@ -50,7 +50,6 @@ pytest.ini
.env
.env.local
examples/
scripts/bump_version.py
code_quality_checks.sh
run_integration_tests.sh

View File

@@ -1,28 +1,35 @@
## PR Title Format
**Please ensure your PR title follows one of these formats:**
**Please ensure your PR title follows [Conventional Commits](https://www.conventionalcommits.org/) format:**
### Version Bumping Prefixes (trigger Docker build + version bump):
- `feat: <description>` - New features (triggers MINOR version bump)
- `fix: <description>` - Bug fixes (triggers PATCH version bump)
- `breaking: <description>` or `BREAKING CHANGE: <description>` - Breaking changes (triggers MAJOR version bump)
- `perf: <description>` - Performance improvements (triggers PATCH version bump)
- `refactor: <description>` - Code refactoring (triggers PATCH version bump)
### Version Bumping Types (trigger semantic release):
- `feat: <description>` - New features **MINOR** version bump (1.1.0 → 1.2.0)
- `fix: <description>` - Bug fixes **PATCH** version bump (1.1.0 → 1.1.1)
- `perf: <description>` - Performance improvements → **PATCH** version bump (1.1.0 → 1.1.1)
### Non-Version Prefixes (no version bump):
- `docs: <description>` - Documentation only
### Breaking Changes (trigger MAJOR version bump):
For breaking changes, use any commit type above with `BREAKING CHANGE:` in the commit body or `!` after the type:
- `feat!: <description>`**MAJOR** version bump (1.1.0 → 2.0.0)
- `fix!: <description>`**MAJOR** version bump (1.1.0 → 2.0.0)
### Non-Versioning Types (no release):
- `build: <description>` - Build system changes
- `chore: <description>` - Maintenance tasks
- `test: <description>` - Test additions/changes
- `ci: <description>` - CI/CD changes
- `style: <description>` - Code style changes
- `docs: <description>` - Documentation only
- `refactor: <description>` - Code refactoring (no functional changes)
- `style: <description>` - Code style/formatting changes
- `test: <description>` - Test additions/changes
### Docker Build Options:
- `docker: <description>` - Force Docker build without version bump
- `docs+docker: <description>` - Documentation + Docker build
- `chore+docker: <description>` - Maintenance + Docker build
- `test+docker: <description>` - Tests + Docker build
- `ci+docker: <description>` - CI changes + Docker build
- `style+docker: <description>` - Style changes + Docker build
### Docker Build Triggering:
Docker builds are **independent** of versioning and trigger based on:
**Automatic**: When PRs modify relevant files:
- Python files (`*.py`), `requirements*.txt`, `pyproject.toml`
- Docker files (`Dockerfile`, `docker-compose.yml`, `.dockerignore`)
**Manual**: Add the `docker-build` label to force builds for any PR.
## Description

View File

@@ -1,248 +0,0 @@
name: Auto Version
on:
pull_request:
types: [closed]
branches: [main]
jobs:
version:
# Only run if PR was merged (not just closed)
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.PAT }}
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Configure git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Determine version bump type
id: bump_type
run: |
PR_TITLE="${{ github.event.pull_request.title }}"
echo "PR Title: $PR_TITLE"
# Convert to lowercase for case-insensitive matching
PR_TITLE_LOWER=$(echo "$PR_TITLE" | tr '[:upper:]' '[:lower:]')
# Determine bump type based on PR title prefix
if [[ "$PR_TITLE_LOWER" =~ ^(breaking|breaking[[:space:]]change): ]]; then
echo "Detected BREAKING CHANGE - major version bump"
echo "bump_type=major" >> $GITHUB_OUTPUT
echo "should_bump=true" >> $GITHUB_OUTPUT
echo "should_build_docker=true" >> $GITHUB_OUTPUT
elif [[ "$PR_TITLE_LOWER" =~ ^feat: ]]; then
echo "Detected new feature - minor version bump"
echo "bump_type=minor" >> $GITHUB_OUTPUT
echo "should_bump=true" >> $GITHUB_OUTPUT
echo "should_build_docker=true" >> $GITHUB_OUTPUT
elif [[ "$PR_TITLE_LOWER" =~ ^(fix|perf|refactor): ]]; then
echo "Detected fix/perf/refactor - patch version bump"
echo "bump_type=patch" >> $GITHUB_OUTPUT
echo "should_bump=true" >> $GITHUB_OUTPUT
echo "should_build_docker=true" >> $GITHUB_OUTPUT
elif [[ "$PR_TITLE_LOWER" =~ ^docker: ]]; then
echo "Detected docker build request - no version bump but build Docker"
echo "bump_type=none" >> $GITHUB_OUTPUT
echo "should_bump=false" >> $GITHUB_OUTPUT
echo "should_build_docker=true" >> $GITHUB_OUTPUT
elif [[ "$PR_TITLE_LOWER" =~ ^(docs|chore|test|ci|style)\+docker: ]]; then
echo "Detected non-versioned change with Docker build request"
echo "bump_type=none" >> $GITHUB_OUTPUT
echo "should_bump=false" >> $GITHUB_OUTPUT
echo "should_build_docker=true" >> $GITHUB_OUTPUT
elif [[ "$PR_TITLE_LOWER" =~ ^(docs|chore|test|ci|style): ]]; then
echo "Detected non-versioned change - no version bump"
echo "bump_type=none" >> $GITHUB_OUTPUT
echo "should_bump=false" >> $GITHUB_OUTPUT
echo "should_build_docker=false" >> $GITHUB_OUTPUT
else
echo "No recognized prefix - no version bump"
echo "bump_type=none" >> $GITHUB_OUTPUT
echo "should_bump=false" >> $GITHUB_OUTPUT
echo "should_build_docker=false" >> $GITHUB_OUTPUT
fi
- name: Get current version
if: steps.bump_type.outputs.should_bump == 'true'
id: current_version
run: |
CURRENT_VERSION=$(python -c "from config import __version__; print(__version__)")
echo "Current version: $CURRENT_VERSION"
echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
- name: Bump version
if: steps.bump_type.outputs.should_bump == 'true'
id: new_version
run: |
python scripts/bump_version.py ${{ steps.bump_type.outputs.bump_type }}
NEW_VERSION=$(python -c "from config import __version__; print(__version__)")
echo "New version: $NEW_VERSION"
echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT
- name: Commit version change
if: steps.bump_type.outputs.should_bump == 'true'
run: |
git add config.py
git commit -m "chore: bump version to ${{ steps.new_version.outputs.version }}
Automated version bump from PR #${{ github.event.pull_request.number }}
${{ github.event.pull_request.title }}
Co-authored-by: ${{ github.event.pull_request.user.login }} <${{ github.event.pull_request.user.id }}+${{ github.event.pull_request.user.login }}@users.noreply.github.com>"
git push
- name: Create git tag
if: steps.bump_type.outputs.should_bump == 'true'
run: |
git tag -a "v${{ steps.new_version.outputs.version }}" -m "Release v${{ steps.new_version.outputs.version }}
Changes in this release:
- ${{ github.event.pull_request.title }}
PR: #${{ github.event.pull_request.number }}
Author: @${{ github.event.pull_request.user.login }}"
git push origin "v${{ steps.new_version.outputs.version }}"
- name: Generate release notes
if: steps.bump_type.outputs.should_bump == 'true'
id: release_notes
run: |
# Extract PR body for release notes
PR_BODY=$(cat << 'EOF'
${{ github.event.pull_request.body }}
EOF
)
# Create release notes
RELEASE_NOTES=$(cat << EOF
## What's Changed
${{ github.event.pull_request.title }} by @${{ github.event.pull_request.user.login }} in #${{ github.event.pull_request.number }}
### Details
$PR_BODY
### Version Info
- Previous version: ${{ steps.current_version.outputs.version }}
- New version: ${{ steps.new_version.outputs.version }}
- Bump type: ${{ steps.bump_type.outputs.bump_type }}
**Full Changelog**: https://github.com/${{ github.repository }}/compare/v${{ steps.current_version.outputs.version }}...v${{ steps.new_version.outputs.version }}
EOF
)
# Save to file for GitHub release
echo "$RELEASE_NOTES" > release_notes.md
- name: Create GitHub release
if: steps.bump_type.outputs.should_bump == 'true'
uses: softprops/action-gh-release@v1
with:
tag_name: v${{ steps.new_version.outputs.version }}
name: Release v${{ steps.new_version.outputs.version }}
body_path: release_notes.md
draft: false
prerelease: false
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Trigger Docker build
if: steps.bump_type.outputs.should_build_docker == 'true'
run: |
echo "🐳 Triggering Docker build and publish workflow"
# The Docker workflow will be triggered by the tag creation (if version bumped)
# or by repository_dispatch (if docker: prefix without version bump)
if [ "${{ steps.bump_type.outputs.should_bump }}" == "false" ]; then
# For docker: prefix without version bump, trigger via repository_dispatch
curl -X POST \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
-H "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/${{ github.repository }}/dispatches" \
-d '{"event_type":"docker-build","client_payload":{"pr_number":"${{ github.event.pull_request.number }}","pr_title":"${{ github.event.pull_request.title }}","commit_sha":"${{ github.sha }}"}}'
# Add comment to PR about Docker build
COMMENT_BODY="🐳 **Docker Image Build Triggered**
This PR triggered a Docker image build because of the \`+docker\` suffix in the title.
**Expected Image Tags:**
- \`ghcr.io/${{ github.repository_owner }}/zen-mcp-server:pr-${{ github.event.pull_request.number }}\`
- \`ghcr.io/${{ github.repository_owner }}/zen-mcp-server:main-${{ github.sha }}\`
**To test the image after build completes:**
\`\`\`bash
docker pull ghcr.io/${{ github.repository_owner }}/zen-mcp-server:pr-${{ github.event.pull_request.number }}
\`\`\`
**Claude Desktop config for testing:**
\`\`\`json
{
\"mcpServers\": {
\"gemini\": {
\"command\": \"docker\",
\"args\": [
\"run\", \"--rm\", \"-i\",
\"-e\", \"GEMINI_API_KEY\",
\"ghcr.io/${{ github.repository_owner }}/zen-mcp-server:pr-${{ github.event.pull_request.number }}\"
],
\"env\": {
\"GEMINI_API_KEY\": \"your-api-key-here\"
}
}
}
}
\`\`\`
View the build progress in the [Actions tab](https://github.com/${{ github.repository }}/actions)."
curl -X POST \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
-H "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments" \
-d "{\"body\":\"$COMMENT_BODY\"}"
fi
- name: Summary
run: |
if [ "${{ steps.bump_type.outputs.should_bump }}" == "true" ]; then
echo "### ✅ Version Bumped Successfully" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Previous version**: ${{ steps.current_version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "- **New version**: ${{ steps.new_version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "- **Bump type**: ${{ steps.bump_type.outputs.bump_type }}" >> $GITHUB_STEP_SUMMARY
echo "- **Tag**: v${{ steps.new_version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "- **PR**: #${{ github.event.pull_request.number }}" >> $GITHUB_STEP_SUMMARY
echo "- **Docker**: Will build and publish with new tag" >> $GITHUB_STEP_SUMMARY
elif [ "${{ steps.bump_type.outputs.should_build_docker }}" == "true" ]; then
echo "### 🐳 Docker Build Requested" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "No version bump but Docker image will be built and published." >> $GITHUB_STEP_SUMMARY
echo "- **PR**: #${{ github.event.pull_request.number }}" >> $GITHUB_STEP_SUMMARY
echo "- **Title**: ${{ github.event.pull_request.title }}" >> $GITHUB_STEP_SUMMARY
echo "- **Docker tag**: Based on commit SHA" >> $GITHUB_STEP_SUMMARY
else
echo "### No Version Bump Required" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "PR title prefix did not require a version bump." >> $GITHUB_STEP_SUMMARY
echo "- **PR**: #${{ github.event.pull_request.number }}" >> $GITHUB_STEP_SUMMARY
echo "- **Title**: ${{ github.event.pull_request.title }}" >> $GITHUB_STEP_SUMMARY
fi

129
.github/workflows/docker-pr.yml vendored Normal file
View File

@@ -0,0 +1,129 @@
name: PR Docker Build
on:
pull_request:
types: [opened, synchronize, reopened, labeled, unlabeled]
paths:
- '**.py'
- 'requirements*.txt'
- 'pyproject.toml'
- 'Dockerfile'
- 'docker-compose.yml'
- '.dockerignore'
permissions:
contents: read
packages: write
pull-requests: write
jobs:
docker:
name: Build Docker Image
runs-on: ubuntu-latest
if: |
github.event.action == 'opened' ||
github.event.action == 'synchronize' ||
github.event.action == 'reopened' ||
contains(github.event.pull_request.labels.*.name, 'docker-build')
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
if: github.event.pull_request.head.repo.full_name == github.repository
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
# PR-specific tag for testing
type=raw,value=pr-${{ github.event.number }}-${{ github.sha }}
type=raw,value=pr-${{ github.event.number }}
- name: Build and push Docker image (internal PRs)
if: github.event.pull_request.head.repo.full_name == github.repository
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
- name: Build Docker image (fork PRs)
if: github.event.pull_request.head.repo.full_name != github.repository
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: false
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Add Docker build comment (internal PRs)
if: github.event.pull_request.head.repo.full_name == github.repository
uses: marocchino/sticky-pull-request-comment@d2ad0de260ae8b0235ce059e63f2949ba9e05943 # v2.9.3
with:
header: docker-build
message: |
## 🐳 Docker Build Complete
**PR**: #${{ github.event.number }} | **Commit**: `${{ github.sha }}`
```
${{ steps.meta.outputs.tags }}
```
**Test:** `docker pull ghcr.io/${{ github.repository }}:pr-${{ github.event.number }}`
**Claude config:**
```json
{
"mcpServers": {
"zen": {
"command": "docker",
"args": ["run", "--rm", "-i", "-e", "GEMINI_API_KEY", "ghcr.io/${{ github.repository }}:pr-${{ github.event.number }}"],
"env": { "GEMINI_API_KEY": "your-key" }
}
}
}
```
💡 Add `docker-build` label to manually trigger builds
- name: Update job summary (internal PRs)
if: github.event.pull_request.head.repo.full_name == github.repository
run: |
{
echo "## 🐳 Docker Build Complete"
echo "**PR**: #${{ github.event.number }} | **Commit**: ${{ github.sha }}"
echo '```'
echo "${{ steps.meta.outputs.tags }}"
echo '```'
} >> $GITHUB_STEP_SUMMARY
- name: Update job summary (fork PRs)
if: github.event.pull_request.head.repo.full_name != github.repository
run: |
{
echo "## 🐳 Docker Build Complete (Build Only)"
echo "**PR**: #${{ github.event.number }} | **Commit**: ${{ github.sha }}"
echo "✅ Multi-platform Docker build successful"
echo "Note: Fork PRs only build (no push) for security"
} >> $GITHUB_STEP_SUMMARY

116
.github/workflows/docker-release.yml vendored Normal file
View File

@@ -0,0 +1,116 @@
name: Docker Release Build
on:
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: 'Tag to build (leave empty for latest release)'
required: false
type: string
permissions:
contents: read
packages: write
jobs:
docker:
name: Build and Push Docker Image
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
# If triggered by workflow_dispatch with a tag, checkout that tag
ref: ${{ inputs.tag || github.event.release.tag_name }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
# Tag with the release version
type=semver,pattern={{version}},value=${{ inputs.tag || github.event.release.tag_name }}
type=semver,pattern={{major}}.{{minor}},value=${{ inputs.tag || github.event.release.tag_name }}
type=semver,pattern={{major}},value=${{ inputs.tag || github.event.release.tag_name }}
# Also tag as latest for the most recent release
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
- name: Update release with Docker info
if: github.event_name == 'release'
run: |
RELEASE_TAG="${{ github.event.release.tag_name }}"
DOCKER_TAGS=$(echo "${{ steps.meta.outputs.tags }}" | tr '\n' ' ')
# Add Docker information to the release
gh release edit "$RELEASE_TAG" --notes-file - << EOF
${{ github.event.release.body }}
---
## 🐳 Docker Images
This release is available as Docker images:
$(echo "$DOCKER_TAGS" | sed 's/ghcr.io/- `ghcr.io/g' | sed 's/ /`\n/g')
**Quick start with Docker:**
\`\`\`bash
docker pull ghcr.io/${{ github.repository }}:$RELEASE_TAG
\`\`\`
**Claude Desktop configuration:**
\`\`\`json
{
"mcpServers": {
"zen-mcp-server": {
"command": "docker",
"args": [
"run", "--rm", "-i",
"-e", "GEMINI_API_KEY",
"ghcr.io/${{ github.repository }}:$RELEASE_TAG"
],
"env": {
"GEMINI_API_KEY": "your-api-key-here"
}
}
}
}
\`\`\`
EOF
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create deployment summary
run: |
echo "## 🐳 Docker Release Build Complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Release**: ${{ inputs.tag || github.event.release.tag_name }}" >> $GITHUB_STEP_SUMMARY
echo "**Images built:**" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
echo "${{ steps.meta.outputs.tags }}" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY

47
.github/workflows/semantic-pr.yml vendored Normal file
View File

@@ -0,0 +1,47 @@
---
name: Semantic PR
on:
pull_request:
types: [opened, edited, synchronize]
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
permissions:
contents: read
pull-requests: write
jobs:
semantic-pr:
name: Validate PR
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Check PR Title
id: lint-pr-title
uses: amannn/action-semantic-pull-request@0723387faaf9b38adef4775cd42cfd5155ed6017 # v5.5.3
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Add PR error comment
uses: marocchino/sticky-pull-request-comment@d2ad0de260ae8b0235ce059e63f2949ba9e05943 # v2.9.3
if: always() && (steps.lint-pr-title.outputs.error_message != null)
with:
header: pr-title-lint-error
message: |
We require pull request titles to follow the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/) and it looks like your proposed title needs to be adjusted.
Details:
```
${{ steps.lint-pr-title.outputs.error_message }}
```
- name: Delete PR error comment
uses: marocchino/sticky-pull-request-comment@d2ad0de260ae8b0235ce059e63f2949ba9e05943 # v2.9.3
if: ${{ steps.lint-pr-title.outputs.error_message == null }}
with:
header: pr-title-lint-error
delete: true

61
.github/workflows/semantic-release.yml vendored Normal file
View File

@@ -0,0 +1,61 @@
name: Semantic Release
on:
push:
branches:
- main
permissions:
contents: write
issues: write
pull-requests: write
jobs:
release:
runs-on: ubuntu-latest
concurrency: release
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
persist-credentials: true
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: "3.11"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install python-semantic-release
- name: Verify tests pass
run: |
pip install -r requirements.txt
pip install -r requirements-dev.txt
python -m pytest tests/ -v --ignore=simulator_tests/ -m "not integration"
- name: Run semantic release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
semantic-release version
semantic-release publish
- name: Upload build artifacts to release
if: hashFiles('dist/*') != ''
run: |
# Get the latest release tag
LATEST_TAG=$(gh release list --limit 1 --json tagName --jq '.[0].tagName')
if [ ! -z "$LATEST_TAG" ]; then
echo "Uploading artifacts to release $LATEST_TAG"
gh release upload "$LATEST_TAG" dist/* --clobber
fi
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,8 +1,6 @@
name: Tests
on:
push:
branches: [ main, develop ]
pull_request:
branches: [main]
@@ -57,4 +55,3 @@ jobs:
- name: Run ruff linter
run: ruff check . --exclude test_simulation_files

38
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,38 @@
---
default_stages: [pre-commit, pre-push]
repos:
- repo: https://github.com/psf/black
rev: 25.1.0
hooks:
- id: black
- repo: https://github.com/pycqa/isort
rev: 6.0.1
hooks:
- id: isort
args: ["--profile", "black"]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.12.8
hooks:
- id: ruff
args: [--fix]
# Configuration for specific tools
default_language_version:
python: python3
# Exclude patterns
exclude: |
(?x)^(
\.git/|
\.venv/|
venv/|
\.zen_venv/|
__pycache__/|
\.pytest_cache/|
logs/|
dist/|
build/|
test_simulation_files/
)

View File

@@ -23,8 +23,16 @@ We maintain high code quality standards. **All contributions must pass our autom
#### Required Code Quality Checks
Before submitting any PR, run our automated quality check script:
**Option 1 - Automated (Recommended):**
```bash
# Install pre-commit hooks (one-time setup)
pre-commit install
# Now linting runs automatically on every commit
# Includes: ruff (with auto-fix), black, isort
```
**Option 2 - Manual:**
```bash
# Run the comprehensive quality checks script
./code_quality_checks.sh

View File

@@ -1,6 +1,6 @@
[project]
name = "zen-mcp-server"
version = "0.1.0"
version = "1.1.0"
description = "AI-powered MCP server with multiple model providers"
requires-python = ">=3.9"
dependencies = [
@@ -84,6 +84,33 @@ ignore = [
"tests/*" = ["B011"]
"tests/conftest.py" = ["E402"] # Module level imports not at top of file - needed for test setup
[tool.semantic_release]
version_toml = ["pyproject.toml:project.version"]
branch = "main"
build_command = "python -m pip install --upgrade build && python -m build"
dist_path = "dist/"
upload_to_vcs_release = true
upload_to_repository = false
remove_dist = false
commit_version_number = true
commit_message = "chore(release): {version}\n\nAutomatically generated by python-semantic-release"
tag_format = "v{version}"
[tool.semantic_release.branches.main]
match = "main"
prerelease = false
[tool.semantic_release.changelog]
exclude_commit_patterns = []
[tool.semantic_release.commit_parser_options]
allowed_tags = ["build", "chore", "ci", "docs", "feat", "fix", "perf", "style", "refactor", "test"]
minor_tags = ["feat"]
patch_tags = ["fix", "perf"]
[tool.semantic_release.remote.token]
env = "GH_TOKEN"
[build-system]
requires = ["setuptools>=45", "wheel", "setuptools_scm[toml]>=6.2"]
build-backend = "setuptools.build_meta"

View File

@@ -4,3 +4,5 @@ pytest-mock>=3.11.0
black>=23.0.0
ruff>=0.1.0
isort>=5.12.0
python-semantic-release>=10.3.0
build>=1.0.0

View File

@@ -1,114 +0,0 @@
#!/usr/bin/env python3
"""
Version bumping utility for Gemini MCP Server
This script handles semantic version bumping for the project by:
- Reading current version from config.py
- Applying the appropriate version bump (major, minor, patch)
- Updating config.py with new version and timestamp
- Preserving file structure and formatting
"""
import re
import sys
from datetime import datetime
from pathlib import Path
def parse_version(version_string: str) -> tuple[int, int, int]:
"""Parse semantic version string into tuple of integers."""
match = re.match(r"^(\d+)\.(\d+)\.(\d+)", version_string)
if not match:
raise ValueError(f"Invalid version format: {version_string}")
return int(match.group(1)), int(match.group(2)), int(match.group(3))
def bump_version(version: tuple[int, int, int], bump_type: str) -> tuple[int, int, int]:
"""Apply version bump according to semantic versioning rules."""
major, minor, patch = version
if bump_type == "major":
return (major + 1, 0, 0)
elif bump_type == "minor":
return (major, minor + 1, 0)
elif bump_type == "patch":
return (major, minor, patch + 1)
else:
raise ValueError(f"Invalid bump type: {bump_type}")
def update_config_file(new_version: str) -> None:
"""Update version and timestamp in config.py while preserving structure."""
config_path = Path(__file__).parent.parent / "config.py"
if not config_path.exists():
raise FileNotFoundError(f"config.py not found at {config_path}")
# Read the current content
content = config_path.read_text()
# Update version using regex to preserve formatting
version_pattern = r'(__version__\s*=\s*["\'])[\d\.]+(["\'])'
content = re.sub(version_pattern, rf"\g<1>{new_version}\g<2>", content)
# Update the __updated__ field with current date
current_date = datetime.now().strftime("%Y-%m-%d")
updated_pattern = r'(__updated__\s*=\s*["\'])[\d\-]+(["\'])'
content = re.sub(updated_pattern, rf"\g<1>{current_date}\g<2>", content)
# Write back the updated content
config_path.write_text(content)
print(f"Updated config.py: version={new_version}, updated={current_date}")
def get_current_version() -> str:
"""Extract current version from config.py."""
config_path = Path(__file__).parent.parent / "config.py"
if not config_path.exists():
raise FileNotFoundError(f"config.py not found at {config_path}")
content = config_path.read_text()
match = re.search(r'__version__\s*=\s*["\']([^"\']+)["\']', content)
if not match:
raise ValueError("Could not find __version__ in config.py")
return match.group(1)
def main():
"""Main entry point for version bumping."""
if len(sys.argv) != 2:
print("Usage: python bump_version.py <major|minor|patch>")
sys.exit(1)
bump_type = sys.argv[1].lower()
if bump_type not in ["major", "minor", "patch"]:
print(f"Invalid bump type: {bump_type}")
print("Valid types: major, minor, patch")
sys.exit(1)
try:
# Get current version
current = get_current_version()
print(f"Current version: {current}")
# Parse and bump version
version_tuple = parse_version(current)
new_version_tuple = bump_version(version_tuple, bump_type)
new_version = f"{new_version_tuple[0]}.{new_version_tuple[1]}.{new_version_tuple[2]}"
# Update config file
update_config_file(new_version)
# Output new version for GitHub Actions
print(f"New version: {new_version}")
except Exception as e:
print(f"Error: {e}")
sys.exit(1)
if __name__ == "__main__":
main()