diff --git a/.bumpversion.cfg b/.bumpversion.cfg new file mode 100644 index 0000000..f559689 --- /dev/null +++ b/.bumpversion.cfg @@ -0,0 +1,14 @@ +[bumpversion] +current_version = 2.0.0 +commit = False +tag = False +parse = (?P\d+)\.(?P\d+)\.(?P\d+) +serialize = {major}.{minor}.{patch} + +[bumpversion:file:pyproject.toml] +search = version = "{current_version}" +replace = version = "{new_version}" + +[bumpversion:file:src/mcp_feedback_enhanced/__init__.py] +search = __version__ = "{current_version}" +replace = __version__ = "{new_version}" \ No newline at end of file diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..38048c6 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,114 @@ +name: Auto Release to PyPI + +on: + workflow_dispatch: + inputs: + version_type: + description: 'Version bump type' + required: true + default: 'patch' + type: choice + options: + - patch # 2.0.0 -> 2.0.1 (bug fixes) + - minor # 2.0.0 -> 2.1.0 (new features) + - major # 2.0.0 -> 3.0.0 (breaking changes) + +jobs: + release: + runs-on: ubuntu-latest + permissions: + contents: write + id-token: write + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Install uv + uses: astral-sh/setup-uv@v4 + with: + version: "latest" + + - name: Set up Python + run: uv python install + + - name: Install dependencies + run: | + uv add --dev bump2version + + - name: Configure Git + run: | + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + + - name: Get current version + id: current_version + run: | + CURRENT_VERSION=$(grep '^version =' pyproject.toml | cut -d'"' -f2) + echo "current=$CURRENT_VERSION" >> $GITHUB_OUTPUT + echo "Current version: $CURRENT_VERSION" + + - name: Bump version + id: bump_version + run: | + uv run bump2version ${{ github.event.inputs.version_type }} + NEW_VERSION=$(grep '^version =' pyproject.toml | cut -d'"' -f2) + echo "new=$NEW_VERSION" >> $GITHUB_OUTPUT + echo "New version: $NEW_VERSION" + + - name: Update __init__.py version + run: | + NEW_VERSION="${{ steps.bump_version.outputs.new }}" + sed -i "s/__version__ = \".*\"/__version__ = \"$NEW_VERSION\"/" src/mcp_feedback_enhanced/__init__.py + + - name: Commit version bump + run: | + git add . + git commit -m "🔖 Release v${{ steps.bump_version.outputs.new }}" + git tag "v${{ steps.bump_version.outputs.new }}" + + - name: Build package + run: uv build + + - name: Check package + run: uv run twine check dist/* + + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} + + - name: Push changes and tags + run: | + git push origin main + git push origin "v${{ steps.bump_version.outputs.new }}" + + - name: Create GitHub Release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: "v${{ steps.bump_version.outputs.new }}" + release_name: "Release v${{ steps.bump_version.outputs.new }}" + body: | + ## Changes in v${{ steps.bump_version.outputs.new }} + + **Version Type:** ${{ github.event.inputs.version_type }} + **Previous Version:** v${{ steps.current_version.outputs.current }} + + This release was automatically generated. + + ### Installation + ```bash + uvx mcp-feedback-enhanced + ``` + + ### What's New + - Auto-generated release from GitHub Actions + - Updated package version from ${{ steps.current_version.outputs.current }} to ${{ steps.bump_version.outputs.new }} + + draft: false + prerelease: false \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 10a2418..068e1cc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,6 +52,7 @@ packages = ["src/mcp_feedback_enhanced"] [tool.uv] dev-dependencies = [ + "bump2version>=1.0.1", "pytest>=7.0.0", "pytest-asyncio>=0.21.0", "twine>=6.1.0", diff --git a/scripts/release.py b/scripts/release.py new file mode 100644 index 0000000..60a909a --- /dev/null +++ b/scripts/release.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +""" +本地發布腳本 +用法: + python scripts/release.py patch # 2.0.0 -> 2.0.1 + python scripts/release.py minor # 2.0.0 -> 2.1.0 + python scripts/release.py major # 2.0.0 -> 3.0.0 +""" + +import subprocess +import sys +import re +from pathlib import Path + +def run_cmd(cmd, check=True): + """執行命令並返回結果""" + print(f"🔨 執行: {cmd}") + result = subprocess.run(cmd, shell=True, capture_output=True, text=True) + if check and result.returncode != 0: + print(f"❌ 錯誤: {result.stderr}") + sys.exit(1) + return result + +def get_current_version(): + """從 pyproject.toml 獲取當前版本""" + pyproject_path = Path("pyproject.toml") + content = pyproject_path.read_text(encoding="utf-8") + match = re.search(r'version = "([^"]+)"', content) + if match: + return match.group(1) + raise ValueError("無法找到版本號") + +def bump_version(version_type): + """更新版本號""" + if version_type not in ['patch', 'minor', 'major']: + print("❌ 版本類型必須是: patch, minor, major") + sys.exit(1) + + current = get_current_version() + print(f"📦 當前版本: {current}") + + # 使用 bump2version + run_cmd(f"uv run bump2version {version_type}") + + new_version = get_current_version() + print(f"🎉 新版本: {new_version}") + + return current, new_version + +def main(): + if len(sys.argv) != 2: + print(__doc__) + sys.exit(1) + + version_type = sys.argv[1] + + print("🚀 開始發布流程...") + + # 檢查是否有未提交的變更 + result = run_cmd("git status --porcelain", check=False) + if result.stdout.strip(): + print("⚠️ 有未提交的變更,請先提交或暫存") + print(result.stdout) + choice = input("是否繼續? (y/N): ") + if choice.lower() != 'y': + sys.exit(1) + + # 更新版本 + old_version, new_version = bump_version(version_type) + + # 建置套件 + print("📦 建置套件...") + run_cmd("uv build") + + # 檢查套件 + print("🔍 檢查套件...") + run_cmd("uv run twine check dist/*") + + # 提交變更 + print("💾 提交版本更新...") + run_cmd("git add .") + run_cmd(f'git commit -m "🔖 Release v{new_version}"') + run_cmd(f'git tag "v{new_version}"') + + # 詢問是否發布 + print(f"\n✅ 準備發布版本 {old_version} -> {new_version}") + choice = input("是否發布到 PyPI? (y/N): ") + + if choice.lower() == 'y': + print("🚀 發布到 PyPI...") + run_cmd("uv run twine upload dist/*") + + print("📤 推送到 GitHub...") + run_cmd("git push origin main") + run_cmd(f'git push origin "v{new_version}"') + + print(f"🎉 發布完成!版本 v{new_version} 已上線") + print(f"📦 安裝命令: uvx mcp-feedback-enhanced") + else: + print("⏸️ 發布已取消,版本已更新但未發布") + print("💡 您可以稍後手動發布: uv run twine upload dist/*") + +if __name__ == "__main__": + main() \ No newline at end of file