diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml new file mode 100644 index 0000000..12242ba --- /dev/null +++ b/.github/workflows/build-and-release.yml @@ -0,0 +1,148 @@ +name: Build Desktop & Release + +on: + workflow_dispatch: + inputs: + version_type: + description: 'Version bump type (ignored if custom_version is provided)' + required: false + default: 'patch' + type: choice + options: + - patch # 2.0.0 -> 2.0.1 (bug fixes, security patches, documentation updates) + - minor # 2.0.0 -> 2.1.0 (new features, enhancements, backward-compatible changes) + - major # 2.0.0 -> 3.0.0 (breaking changes, architecture refactoring, API changes) + custom_version: + description: 'Custom version number (e.g., 2.5.0) - overrides version_type if provided' + required: false + type: string + platforms: + description: '選擇要構建的平台' + required: true + default: 'all' + type: choice + options: + - all + - windows + - macos + - linux + skip_release: + description: '只構建桌面應用,不進行發佈' + required: false + default: false + type: boolean + +jobs: + # 第一步:構建桌面應用 + build-desktop: + name: Build Desktop Applications + uses: ./.github/workflows/build-desktop.yml + with: + platforms: ${{ github.event.inputs.platforms }} + upload_artifacts: true + + # 第二步:等待構建完成並檢查結果 + check-build: + name: Check Build Results + needs: build-desktop + runs-on: ubuntu-latest + outputs: + can_release: ${{ steps.check.outputs.can_release }} + build_run_id: ${{ steps.check.outputs.build_run_id }} + + steps: + - name: Check build results + id: check + run: | + echo "🔍 檢查桌面應用構建結果..." + + # 檢查構建是否成功 + if [ "${{ needs.build-desktop.result }}" = "success" ]; then + echo "✅ 桌面應用構建成功" + echo "can_release=true" >> $GITHUB_OUTPUT + echo "build_run_id=${{ github.run_id }}" >> $GITHUB_OUTPUT + else + echo "❌ 桌面應用構建失敗" + echo "can_release=false" >> $GITHUB_OUTPUT + exit 1 + fi + + - name: Generate summary + run: | + echo "## 🖥️ 桌面應用構建完成" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ✅ 構建結果" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **狀態**: 成功 ✅" >> $GITHUB_STEP_SUMMARY + echo "- **平台**: ${{ github.event.inputs.platforms }}" >> $GITHUB_STEP_SUMMARY + echo "- **Run ID**: ${{ github.run_id }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + if [ "${{ github.event.inputs.skip_release }}" = "true" ]; then + echo "### ⏭️ 跳過發佈" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "根據設置,已跳過自動發佈。" >> $GITHUB_STEP_SUMMARY + echo "如需發佈,請手動運行 [Auto Release to PyPI](../../actions/workflows/publish.yml) 工作流程。" >> $GITHUB_STEP_SUMMARY + else + echo "### 🚀 準備發佈" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "桌面應用構建成功,正在準備自動發佈..." >> $GITHUB_STEP_SUMMARY + fi + + # 第三步:自動發佈(如果沒有跳過) + release: + name: Auto Release to PyPI + needs: [build-desktop, check-build] + if: ${{ needs.check-build.outputs.can_release == 'true' && github.event.inputs.skip_release != 'true' }} + uses: ./.github/workflows/publish.yml + with: + version_type: ${{ github.event.inputs.version_type }} + custom_version: ${{ github.event.inputs.custom_version }} + include_desktop: true + desktop_build_run_id: ${{ needs.check-build.outputs.build_run_id }} + secrets: inherit + + # 最終摘要 + summary: + name: Workflow Summary + needs: [build-desktop, check-build, release] + runs-on: ubuntu-latest + if: always() + + steps: + - name: Generate final summary + run: | + echo "## 🎯 構建和發佈摘要" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # 桌面構建狀態 + echo "### 🖥️ 桌面應用構建" >> $GITHUB_STEP_SUMMARY + if [ "${{ needs.build-desktop.result }}" = "success" ]; then + echo "- ✅ **成功** - 所有選定平台構建完成" >> $GITHUB_STEP_SUMMARY + else + echo "- ❌ **失敗** - 構建過程中出現錯誤" >> $GITHUB_STEP_SUMMARY + fi + + # 發佈狀態 + echo "" >> $GITHUB_STEP_SUMMARY + echo "### 📦 發佈狀態" >> $GITHUB_STEP_SUMMARY + if [ "${{ github.event.inputs.skip_release }}" = "true" ]; then + echo "- ⏭️ **已跳過** - 根據用戶設置跳過發佈" >> $GITHUB_STEP_SUMMARY + elif [ "${{ needs.release.result }}" = "success" ]; then + echo "- ✅ **成功** - 已發佈到 PyPI 和 GitHub Releases" >> $GITHUB_STEP_SUMMARY + elif [ "${{ needs.release.result }}" = "failure" ]; then + echo "- ❌ **失敗** - 發佈過程中出現錯誤" >> $GITHUB_STEP_SUMMARY + elif [ "${{ needs.release.result }}" = "skipped" ]; then + echo "- ⏭️ **已跳過** - 由於桌面構建失敗而跳過" >> $GITHUB_STEP_SUMMARY + else + echo "- ⏳ **進行中** - 發佈正在進行中" >> $GITHUB_STEP_SUMMARY + fi + + echo "" >> $GITHUB_STEP_SUMMARY + echo "### 🔗 相關鏈接" >> $GITHUB_STEP_SUMMARY + echo "- [桌面構建工作流程](../../actions/workflows/build-desktop.yml)" >> $GITHUB_STEP_SUMMARY + echo "- [發佈工作流程](../../actions/workflows/publish.yml)" >> $GITHUB_STEP_SUMMARY + if [ "${{ needs.release.result }}" = "success" ]; then + echo "- [PyPI 頁面](https://pypi.org/project/mcp-feedback-enhanced/)" >> $GITHUB_STEP_SUMMARY + echo "- [GitHub Releases](../../releases)" >> $GITHUB_STEP_SUMMARY + fi diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index 74b77f8..20885b4 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -97,18 +97,47 @@ jobs: ${{ runner.os }}-cargo-${{ matrix.target }}- ${{ runner.os }}-cargo- + - name: Install platform-specific dependencies (Linux) + if: matrix.os == 'ubuntu-latest' + run: | + sudo apt-get update + sudo apt-get install -y \ + libwebkit2gtk-4.1-dev \ + libappindicator3-dev \ + librsvg2-dev \ + patchelf \ + libgtk-3-dev \ + libayatana-appindicator3-dev + - name: Build desktop application for ${{ matrix.name }} run: | cd src-tauri + echo "🔨 開始構建 ${{ matrix.name }} (${{ matrix.target }})..." cargo build --release --target ${{ matrix.target }} --bin mcp-feedback-enhanced-desktop + echo "✅ 構建完成" - name: Verify build output run: | echo "🔍 檢查構建產物..." - if [ "${{ matrix.os }}" = "windows-latest" ]; then - ls -la "src-tauri/target/${{ matrix.target }}/release/${{ matrix.binary }}" || echo "❌ Windows 二進制文件不存在" + BINARY_PATH="src-tauri/target/${{ matrix.target }}/release/${{ matrix.binary }}" + + if [ -f "$BINARY_PATH" ]; then + echo "✅ 找到二進制文件: $BINARY_PATH" + ls -la "$BINARY_PATH" + + # 檢查文件大小 + FILE_SIZE=$(stat -f%z "$BINARY_PATH" 2>/dev/null || stat -c%s "$BINARY_PATH" 2>/dev/null || echo "unknown") + echo "📏 文件大小: $FILE_SIZE bytes" + + # 檢查文件類型 (僅在 Linux/macOS) + if [ "${{ matrix.os }}" != "windows-latest" ]; then + file "$BINARY_PATH" || echo "無法檢查文件類型" + fi else - ls -la "src-tauri/target/${{ matrix.target }}/release/${{ matrix.binary }}" || echo "❌ ${{ matrix.name }} 二進制文件不存在" + echo "❌ ${{ matrix.name }} 二進制文件不存在: $BINARY_PATH" + echo "🔍 檢查目標目錄內容:" + ls -la "src-tauri/target/${{ matrix.target }}/release/" || echo "目標目錄不存在" + exit 1 fi shell: bash @@ -120,14 +149,38 @@ jobs: path: src-tauri/target/${{ matrix.target }}/release/${{ matrix.binary }} retention-days: 30 # 保留 30 天 compression-level: 6 + if-no-files-found: error # 如果沒有找到文件則失敗 - # 構建摘要 + # 構建摘要和驗證 build-summary: needs: build-desktop runs-on: ubuntu-latest if: always() + outputs: + build_success: ${{ steps.check_results.outputs.success }} + platforms_built: ${{ steps.check_results.outputs.platforms }} steps: + - name: Check build results + id: check_results + run: | + echo "🔍 檢查構建結果..." + + # 檢查構建狀態 + BUILD_SUCCESS="false" + PLATFORMS_BUILT="" + + if [ "${{ needs.build-desktop.result }}" = "success" ]; then + BUILD_SUCCESS="true" + PLATFORMS_BUILT="windows,macos-intel,macos-arm64,linux" + echo "✅ 所有平台構建成功" + else + echo "❌ 構建失敗或部分失敗" + fi + + echo "success=$BUILD_SUCCESS" >> $GITHUB_OUTPUT + echo "platforms=$PLATFORMS_BUILT" >> $GITHUB_OUTPUT + - name: Generate build summary run: | echo "## 🖥️ 桌面應用構建摘要" >> $GITHUB_STEP_SUMMARY @@ -137,16 +190,43 @@ jobs: # 檢查各平台構建狀態 if [ "${{ needs.build-desktop.result }}" = "success" ]; then - echo "✅ 所有平台構建成功" >> $GITHUB_STEP_SUMMARY + echo "✅ **所有平台構建成功**" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| 平台 | 狀態 | 產物名稱 |" >> $GITHUB_STEP_SUMMARY + echo "|------|------|----------|" >> $GITHUB_STEP_SUMMARY + echo "| Windows x64 | ✅ 成功 | \`desktop-windows\` |" >> $GITHUB_STEP_SUMMARY + echo "| macOS Intel | ✅ 成功 | \`desktop-macos-intel\` |" >> $GITHUB_STEP_SUMMARY + echo "| macOS ARM64 | ✅ 成功 | \`desktop-macos-arm64\` |" >> $GITHUB_STEP_SUMMARY + echo "| Linux x64 | ✅ 成功 | \`desktop-linux\` |" >> $GITHUB_STEP_SUMMARY elif [ "${{ needs.build-desktop.result }}" = "failure" ]; then - echo "❌ 部分平台構建失敗" >> $GITHUB_STEP_SUMMARY + echo "❌ **部分平台構建失敗**" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "請檢查各平台的構建日誌以了解失敗原因。" >> $GITHUB_STEP_SUMMARY else - echo "⚠️ 構建狀態: ${{ needs.build-desktop.result }}" >> $GITHUB_STEP_SUMMARY + echo "⚠️ **構建狀態**: ${{ needs.build-desktop.result }}" >> $GITHUB_STEP_SUMMARY fi echo "" >> $GITHUB_STEP_SUMMARY - echo "### 下一步" >> $GITHUB_STEP_SUMMARY + echo "### 📦 產物信息" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY - echo "- 構建產物已上傳到 GitHub Artifacts" >> $GITHUB_STEP_SUMMARY - echo "- 可以在發佈流程中使用這些二進制文件" >> $GITHUB_STEP_SUMMARY - echo "- 如需重新構建,請手動觸發此工作流程" >> $GITHUB_STEP_SUMMARY + echo "- **保留期限**: 30 天" >> $GITHUB_STEP_SUMMARY + echo "- **下載位置**: GitHub Actions Artifacts" >> $GITHUB_STEP_SUMMARY + echo "- **使用方式**: 在發佈工作流程中自動下載" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### 🚀 下一步" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + if [ "${{ needs.build-desktop.result }}" = "success" ]; then + echo "✅ **可以進行發佈**" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "1. 前往 [Auto Release to PyPI](../../actions/workflows/publish.yml)" >> $GITHUB_STEP_SUMMARY + echo "2. 點擊 'Run workflow'" >> $GITHUB_STEP_SUMMARY + echo "3. 設置 'include_desktop' 為 true" >> $GITHUB_STEP_SUMMARY + echo "4. 可選:指定此次構建的 Run ID: \`${{ github.run_id }}\`" >> $GITHUB_STEP_SUMMARY + else + echo "❌ **請修復構建問題後重新運行**" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- 檢查構建日誌中的錯誤信息" >> $GITHUB_STEP_SUMMARY + echo "- 修復問題後重新觸發此工作流程" >> $GITHUB_STEP_SUMMARY + echo "- 確保所有平台都能成功構建後再進行發佈" >> $GITHUB_STEP_SUMMARY + fi diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 3a148cc..3704a79 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -287,93 +287,170 @@ jobs: - Auto-generated release from workflow" git tag "v${{ steps.bump_version.outputs.new }}" - - name: Download desktop binaries from latest build + - name: Check desktop build availability + if: ${{ github.event.inputs.include_desktop == 'true' }} + id: check_desktop + run: | + echo "🔍 檢查桌面應用構建可用性..." + + # 如果指定了 run_id,使用指定的構建 + if [ -n "${{ github.event.inputs.desktop_build_run_id }}" ]; then + echo "🎯 使用指定的構建 Run ID: ${{ github.event.inputs.desktop_build_run_id }}" + echo "run_id=${{ github.event.inputs.desktop_build_run_id }}" >> $GITHUB_OUTPUT + else + echo "🔍 查找最新的成功桌面構建..." + + # 使用 GitHub API 查找最新的成功構建 + LATEST_RUN=$(curl -s \ + -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ + -H "Accept: application/vnd.github.v3+json" \ + "https://api.github.com/repos/${{ github.repository }}/actions/workflows/build-desktop.yml/runs?status=success&per_page=1" \ + | jq -r '.workflow_runs[0].id // empty') + + if [ -n "$LATEST_RUN" ] && [ "$LATEST_RUN" != "null" ]; then + echo "✅ 找到最新成功構建: $LATEST_RUN" + echo "run_id=$LATEST_RUN" >> $GITHUB_OUTPUT + else + echo "❌ 沒有找到成功的桌面構建" + echo "💡 請先運行 'Build Desktop Applications' 工作流程" + exit 1 + fi + fi + + - name: Download desktop binaries if: ${{ github.event.inputs.include_desktop == 'true' }} uses: dawidd6/action-download-artifact@v6 with: github_token: ${{ secrets.GITHUB_TOKEN }} workflow: build-desktop.yml - run_id: ${{ github.event.inputs.desktop_build_run_id }} + run_id: ${{ steps.check_desktop.outputs.run_id }} path: desktop-artifacts - if_no_artifact_found: warn + if_no_artifact_found: error - name: Prepare multi-platform desktop binaries if: ${{ github.event.inputs.include_desktop == 'true' }} run: | - # 檢查是否有桌面應用構建產物 - if [ ! -d "desktop-artifacts" ] || [ -z "$(ls -A desktop-artifacts 2>/dev/null)" ]; then - echo "⚠️ 警告:沒有找到桌面應用構建產物" - echo "💡 提示:請先運行 'Build Desktop Applications' 工作流程" - echo "🔧 或者設置 include_desktop 為 false 來跳過桌面應用" + echo "📦 準備多平台桌面應用二進制文件..." + + # 檢查下載的產物 + echo "🔍 檢查下載的桌面應用產物..." + if [ ! -d "desktop-artifacts" ]; then + echo "❌ 桌面產物目錄不存在" exit 1 fi + echo "📁 產物目錄內容:" + find desktop-artifacts -type f -name "*" | head -20 + # 創建桌面應用目錄 mkdir -p src/mcp_feedback_enhanced/desktop_release - # 複製所有平台的二進制文件並重命名 - echo "📦 準備多平台桌面應用二進制文件..." + # 定義平台映射 + declare -A PLATFORM_MAP=( + ["desktop-windows"]="mcp-feedback-enhanced-desktop.exe" + ["desktop-macos-intel"]="mcp-feedback-enhanced-desktop-macos-intel" + ["desktop-macos-arm64"]="mcp-feedback-enhanced-desktop-macos-arm64" + ["desktop-linux"]="mcp-feedback-enhanced-desktop-linux" + ) - # 檢查並複製各平台文件 - if [ -f "desktop-artifacts/desktop-windows/mcp-feedback-enhanced-desktop.exe" ]; then - cp desktop-artifacts/desktop-windows/mcp-feedback-enhanced-desktop.exe src/mcp_feedback_enhanced/desktop_release/ - echo "✅ Windows 二進制文件已複製" - else - echo "⚠️ Windows 二進制文件不存在" - fi + # 複製並重命名二進制文件 + COPIED_COUNT=0 + TOTAL_PLATFORMS=4 - if [ -f "desktop-artifacts/desktop-macos-intel/mcp-feedback-enhanced-desktop" ]; then - cp desktop-artifacts/desktop-macos-intel/mcp-feedback-enhanced-desktop src/mcp_feedback_enhanced/desktop_release/mcp-feedback-enhanced-desktop-macos-intel - echo "✅ macOS Intel 二進制文件已複製" - else - echo "⚠️ macOS Intel 二進制文件不存在" - fi + for platform_dir in desktop-windows desktop-macos-intel desktop-macos-arm64 desktop-linux; do + echo "🔍 處理平台: $platform_dir" - if [ -f "desktop-artifacts/desktop-macos-arm64/mcp-feedback-enhanced-desktop" ]; then - cp desktop-artifacts/desktop-macos-arm64/mcp-feedback-enhanced-desktop src/mcp_feedback_enhanced/desktop_release/mcp-feedback-enhanced-desktop-macos-arm64 - echo "✅ macOS ARM64 二進制文件已複製" - else - echo "⚠️ macOS ARM64 二進制文件不存在" - fi + # 查找該平台的二進制文件 + BINARY_FILE="" + if [ -d "desktop-artifacts/$platform_dir" ]; then + # 查找二進制文件(可能是 .exe 或無擴展名) + BINARY_FILE=$(find "desktop-artifacts/$platform_dir" -name "mcp-feedback-enhanced-desktop*" -type f | head -1) + fi - if [ -f "desktop-artifacts/desktop-linux/mcp-feedback-enhanced-desktop" ]; then - cp desktop-artifacts/desktop-linux/mcp-feedback-enhanced-desktop src/mcp_feedback_enhanced/desktop_release/mcp-feedback-enhanced-desktop-linux - echo "✅ Linux 二進制文件已複製" - else - echo "⚠️ Linux 二進制文件不存在" - fi + if [ -n "$BINARY_FILE" ] && [ -f "$BINARY_FILE" ]; then + TARGET_NAME="${PLATFORM_MAP[$platform_dir]}" + cp "$BINARY_FILE" "src/mcp_feedback_enhanced/desktop_release/$TARGET_NAME" - # 設置執行權限 - chmod +x src/mcp_feedback_enhanced/desktop_release/mcp-feedback-enhanced-desktop-* 2>/dev/null || true + # 設置執行權限(非 Windows) + if [[ "$TARGET_NAME" != *.exe ]]; then + chmod +x "src/mcp_feedback_enhanced/desktop_release/$TARGET_NAME" + fi + + echo "✅ $platform_dir: $BINARY_FILE -> $TARGET_NAME" + COPIED_COUNT=$((COPIED_COUNT + 1)) + else + echo "⚠️ $platform_dir: 未找到二進制文件" + fi + done # 創建 __init__.py echo '"""桌面應用程式二進制檔案"""' > src/mcp_feedback_enhanced/desktop_release/__init__.py - # 顯示文件列表 + # 驗證結果 + echo "" + echo "📊 複製結果統計:" + echo " 成功複製: $COPIED_COUNT/$TOTAL_PLATFORMS 個平台" + + if [ $COPIED_COUNT -eq 0 ]; then + echo "❌ 沒有成功複製任何平台的二進制文件" + exit 1 + elif [ $COPIED_COUNT -lt $TOTAL_PLATFORMS ]; then + echo "⚠️ 部分平台缺失,但繼續發佈" + else + echo "✅ 所有平台都已成功複製" + fi + + # 顯示最終文件列表 + echo "" echo "📦 最終的桌面應用二進制文件:" ls -la src/mcp_feedback_enhanced/desktop_release/ - - name: Build desktop applications locally (fallback) + - name: Validate desktop binaries if: ${{ github.event.inputs.include_desktop == 'true' }} run: | - # 如果沒有預構建的桌面應用,嘗試本地構建 - if [ ! -d "src/mcp_feedback_enhanced/desktop_release" ] || [ -z "$(ls -A src/mcp_feedback_enhanced/desktop_release 2>/dev/null)" ]; then - echo "🔧 沒有找到預構建的桌面應用,嘗試本地構建..." - echo "⚠️ 注意:本地構建可能只支援當前平台" + echo "🔍 驗證桌面應用二進制文件..." - # 安裝 Rust(如果還沒安裝) - if ! command -v cargo &> /dev/null; then - echo "📦 安裝 Rust..." - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y - source ~/.cargo/env + if [ ! -d "src/mcp_feedback_enhanced/desktop_release" ]; then + echo "❌ 桌面應用目錄不存在" + exit 1 + fi + + # 檢查各平台文件 + PLATFORMS=( + "mcp-feedback-enhanced-desktop.exe:Windows" + "mcp-feedback-enhanced-desktop-macos-intel:macOS Intel" + "mcp-feedback-enhanced-desktop-macos-arm64:macOS ARM64" + "mcp-feedback-enhanced-desktop-linux:Linux" + ) + + VALID_COUNT=0 + for platform_info in "${PLATFORMS[@]}"; do + IFS=':' read -r filename description <<< "$platform_info" + filepath="src/mcp_feedback_enhanced/desktop_release/$filename" + + if [ -f "$filepath" ]; then + filesize=$(stat -f%z "$filepath" 2>/dev/null || stat -c%s "$filepath" 2>/dev/null || echo "0") + if [ "$filesize" -gt 1000000 ]; then # 至少 1MB + echo "✅ $description: $filename (${filesize} bytes)" + VALID_COUNT=$((VALID_COUNT + 1)) + else + echo "⚠️ $description: $filename 文件太小 (${filesize} bytes)" + fi + else + echo "❌ $description: $filename 不存在" fi + done - # 運行本地構建腳本 - python scripts/build_desktop.py --release + echo "" + echo "📊 驗證結果: $VALID_COUNT/4 個平台有效" - echo "✅ 本地桌面應用構建完成" - else - echo "✅ 使用預構建的桌面應用" + if [ $VALID_COUNT -eq 0 ]; then + echo "❌ 沒有有效的桌面應用二進制文件" + echo "💡 建議:" + echo " 1. 檢查 'Build Desktop Applications' 工作流程是否成功" + echo " 2. 確認指定的 Run ID 是否正確" + echo " 3. 或者設置 include_desktop 為 false" + exit 1 fi - name: Skip desktop applications @@ -426,10 +503,37 @@ jobs: if [ -d "src/mcp_feedback_enhanced/desktop_release" ] && [ -n "$(ls -A src/mcp_feedback_enhanced/desktop_release 2>/dev/null)" ]; then echo " ✅ 桌面應用已包含在發佈中" echo " 📱 支援的平台:" - [ -f "src/mcp_feedback_enhanced/desktop_release/mcp-feedback-enhanced-desktop.exe" ] && echo " - Windows x64" - [ -f "src/mcp_feedback_enhanced/desktop_release/mcp-feedback-enhanced-desktop-macos-intel" ] && echo " - macOS Intel" - [ -f "src/mcp_feedback_enhanced/desktop_release/mcp-feedback-enhanced-desktop-macos-arm64" ] && echo " - macOS Apple Silicon" - [ -f "src/mcp_feedback_enhanced/desktop_release/mcp-feedback-enhanced-desktop-linux" ] && echo " - Linux x64" + + # 檢查各平台並顯示文件大小 + if [ -f "src/mcp_feedback_enhanced/desktop_release/mcp-feedback-enhanced-desktop.exe" ]; then + size=$(stat -f%z "src/mcp_feedback_enhanced/desktop_release/mcp-feedback-enhanced-desktop.exe" 2>/dev/null || stat -c%s "src/mcp_feedback_enhanced/desktop_release/mcp-feedback-enhanced-desktop.exe" 2>/dev/null || echo "unknown") + echo " - ✅ Windows x64 (${size} bytes)" + else + echo " - ❌ Windows x64 (缺失)" + fi + + if [ -f "src/mcp_feedback_enhanced/desktop_release/mcp-feedback-enhanced-desktop-macos-intel" ]; then + size=$(stat -f%z "src/mcp_feedback_enhanced/desktop_release/mcp-feedback-enhanced-desktop-macos-intel" 2>/dev/null || stat -c%s "src/mcp_feedback_enhanced/desktop_release/mcp-feedback-enhanced-desktop-macos-intel" 2>/dev/null || echo "unknown") + echo " - ✅ macOS Intel (${size} bytes)" + else + echo " - ❌ macOS Intel (缺失)" + fi + + if [ -f "src/mcp_feedback_enhanced/desktop_release/mcp-feedback-enhanced-desktop-macos-arm64" ]; then + size=$(stat -f%z "src/mcp_feedback_enhanced/desktop_release/mcp-feedback-enhanced-desktop-macos-arm64" 2>/dev/null || stat -c%s "src/mcp_feedback_enhanced/desktop_release/mcp-feedback-enhanced-desktop-macos-arm64" 2>/dev/null || echo "unknown") + echo " - ✅ macOS Apple Silicon (${size} bytes)" + else + echo " - ❌ macOS Apple Silicon (缺失)" + fi + + if [ -f "src/mcp_feedback_enhanced/desktop_release/mcp-feedback-enhanced-desktop-linux" ]; then + size=$(stat -f%z "src/mcp_feedback_enhanced/desktop_release/mcp-feedback-enhanced-desktop-linux" 2>/dev/null || stat -c%s "src/mcp_feedback_enhanced/desktop_release/mcp-feedback-enhanced-desktop-linux" 2>/dev/null || echo "unknown") + echo " - ✅ Linux x64 (${size} bytes)" + else + echo " - ❌ Linux x64 (缺失)" + fi + + echo " 🔗 構建來源: Run ID ${{ steps.check_desktop.outputs.run_id }}" else echo " ⚠️ 桌面應用未包含(可能構建失敗)" fi diff --git a/docs/WORKFLOWS.md b/docs/WORKFLOWS.md index 06e155f..be53b1f 100644 --- a/docs/WORKFLOWS.md +++ b/docs/WORKFLOWS.md @@ -86,15 +86,52 @@ ls -la src/mcp_feedback_enhanced/desktop_app/ - 測試安裝: `uvx mcp-feedback-enhanced@latest` - 測試桌面模式: `uvx mcp-feedback-enhanced@latest test --desktop` +## 🚀 一鍵構建和發佈 + +### Build Desktop & Release 工作流程 + +最簡單的方式是使用 **Build Desktop & Release** 工作流程,它會自動: +1. 構建所有平台的桌面應用 +2. 等待構建完成 +3. 自動觸發發佈流程 + +**使用方法**: +1. 前往 [Build Desktop & Release](../../actions/workflows/build-and-release.yml) +2. 點擊 "Run workflow" +3. 選擇版本類型或輸入自定義版本 +4. 選擇要構建的平台(默認:all) +5. 如果只想構建不發佈,勾選 "只構建桌面應用,不進行發佈" + +**優勢**: +- ✅ 自動化整個流程 +- ✅ 確保桌面應用構建成功後才發佈 +- ✅ 統一的狀態報告 +- ✅ 減少手動操作錯誤 + ## 🔧 故障排除 ### 桌面應用構建失敗 1. **檢查構建日誌** - 查看 GitHub Actions 中的詳細錯誤信息 2. **平台特定問題**: - - macOS: 可能缺少 Xcode 命令行工具 - - Linux: 可能缺少系統依賴 (GTK, Cairo 等) - - Windows: 通常構建成功 + - **macOS**: 可能缺少 Xcode 命令行工具或系統依賴 + - **Linux**: 可能缺少系統依賴 (GTK, WebKit, Cairo 等) + - **Windows**: 通常構建成功,如失敗檢查 MSVC 工具鏈 + +### 發佈流程問題 + +1. **桌面應用缺失**: + - 確認 "Build Desktop Applications" 工作流程已成功運行 + - 檢查指定的 Run ID 是否正確 + - 驗證 Artifacts 是否已正確上傳 + +2. **版本衝突**: + - 檢查 PyPI 上是否已存在相同版本 + - 確認版本號格式正確 (X.Y.Z) + +3. **權限問題**: + - 確認 PYPI_API_TOKEN 密鑰已正確設置 + - 檢查 GitHub Token 權限 3. **本地測試** - 在對應平台上運行本地構建腳本 diff --git a/pyproject.toml b/pyproject.toml index c585834..d87a069 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,13 +1,13 @@ [project] name = "mcp-feedback-enhanced" version = "2.4.3" -description = "Enhanced MCP server for interactive user feedback and command execution in AI-assisted development, featuring Web UI with intelligent environment detection." +description = "Enhanced MCP server for interactive user feedback and command execution in AI-assisted development, featuring dual interface support (Web UI and Desktop Application) with intelligent environment detection and cross-platform compatibility." readme = "README.md" requires-python = ">=3.11" authors = [ { name = "Minidoracat", email = "minidora0702@gmail.com" } ] -keywords = ["mcp", "ai", "feedback", "web-ui", "interactive", "development"] +keywords = ["mcp", "ai", "feedback", "web-ui", "desktop-app", "interactive", "development", "cross-platform", "tauri", "dual-interface"] classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", @@ -17,6 +17,13 @@ classifiers = [ "Programming Language :: Python :: 3.12", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Software Development :: User Interfaces", + "Topic :: Desktop Environment", + "Topic :: Internet :: WWW/HTTP :: Dynamic Content", + "Operating System :: OS Independent", + "Environment :: Web Environment", + "Environment :: X11 Applications", + "Environment :: Win32 (MS Windows)", + "Environment :: MacOS X", ] dependencies = [ "fastmcp>=2.0.0", diff --git a/scripts/validate_workflows.py b/scripts/validate_workflows.py new file mode 100644 index 0000000..264d000 --- /dev/null +++ b/scripts/validate_workflows.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python3 +""" +GitHub Actions 工作流程驗證腳本 + +此腳本驗證 GitHub Actions 工作流程文件的語法和配置正確性。 +""" + +import sys +from pathlib import Path + +import yaml + + +def validate_yaml_syntax(file_path: Path) -> bool: + """驗證 YAML 文件語法""" + try: + with open(file_path, encoding="utf-8") as f: + yaml.safe_load(f) + print(f"✅ {file_path.name}: YAML 語法正確") + return True + except yaml.YAMLError as e: + print(f"❌ {file_path.name}: YAML 語法錯誤 - {e}") + return False + except Exception as e: + print(f"❌ {file_path.name}: 讀取文件失敗 - {e}") + return False + + +def validate_workflow_structure(file_path: Path) -> bool: + """驗證工作流程結構""" + try: + with open(file_path, encoding="utf-8") as f: + workflow = yaml.safe_load(f) + + # 檢查是否成功解析 + if workflow is None: + print(f"❌ {file_path.name}: 文件為空或解析失敗") + return False + + # 檢查必需的頂級字段 + # 注意:YAML 會將 'on' 解析為 True,所以我們需要特殊處理 + required_fields = ["name", "jobs"] + for field in required_fields: + if field not in workflow: + print(f"❌ {file_path.name}: 缺少必需字段 '{field}'") + print(f" 實際字段: {list(workflow.keys())}") + return False + + # 檢查 'on' 字段(可能被解析為 True) + if "on" not in workflow and True not in workflow: + print(f"❌ {file_path.name}: 缺少觸發條件 'on'") + print(f" 實際字段: {list(workflow.keys())}") + return False + + # 檢查 jobs 結構 + if not isinstance(workflow["jobs"], dict): + print(f"❌ {file_path.name}: 'jobs' 必須是字典") + return False + + if not workflow["jobs"]: + print(f"❌ {file_path.name}: 'jobs' 不能為空") + return False + + print(f"✅ {file_path.name}: 工作流程結構正確") + return True + + except Exception as e: + print(f"❌ {file_path.name}: 結構驗證失敗 - {e}") + return False + + +def validate_build_desktop_workflow(file_path: Path) -> bool: + """驗證桌面構建工作流程的特定配置""" + try: + with open(file_path, encoding="utf-8") as f: + workflow = yaml.safe_load(f) + + # 檢查 matrix 配置 + build_job = workflow["jobs"].get("build-desktop", {}) + strategy = build_job.get("strategy", {}) + matrix = strategy.get("matrix", {}) + + if "include" not in matrix: + print(f"❌ {file_path.name}: 缺少 matrix.include 配置") + return False + + # 檢查平台配置 + platforms = matrix["include"] + expected_platforms = {"windows", "macos-intel", "macos-arm64", "linux"} + actual_platforms = {item.get("name") for item in platforms} + + if not expected_platforms.issubset(actual_platforms): + missing = expected_platforms - actual_platforms + print(f"❌ {file_path.name}: 缺少平台配置: {missing}") + return False + + print(f"✅ {file_path.name}: 桌面構建配置正確") + return True + + except Exception as e: + print(f"❌ {file_path.name}: 桌面構建驗證失敗 - {e}") + return False + + +def validate_publish_workflow(file_path: Path) -> bool: + """驗證發佈工作流程的特定配置""" + try: + with open(file_path, encoding="utf-8") as f: + workflow = yaml.safe_load(f) + + # 檢查輸入參數 - 注意 'on' 可能被解析為 True + on_section = workflow.get("on") or workflow.get(True) + if not on_section: + print(f"❌ {file_path.name}: 找不到觸發條件") + return False + + workflow_dispatch = on_section.get("workflow_dispatch", {}) + inputs = workflow_dispatch.get("inputs", {}) + + required_inputs = {"version_type", "include_desktop"} + actual_inputs = set(inputs.keys()) + + if not required_inputs.issubset(actual_inputs): + missing = required_inputs - actual_inputs + print(f"❌ {file_path.name}: 缺少輸入參數: {missing}") + print(f" 實際輸入參數: {actual_inputs}") + return False + + # 檢查是否有桌面應用處理步驟 + release_job = workflow["jobs"].get("release", {}) + steps = release_job.get("steps", []) + + has_desktop_steps = any( + "desktop" in step.get("name", "").lower() for step in steps + ) + + if not has_desktop_steps: + print(f"❌ {file_path.name}: 缺少桌面應用處理步驟") + return False + + print(f"✅ {file_path.name}: 發佈工作流程配置正確") + return True + + except Exception as e: + print(f"❌ {file_path.name}: 發佈工作流程驗證失敗 - {e}") + return False + + +def main(): + """主函數""" + print("🔍 驗證 GitHub Actions 工作流程...") + print() + + # 獲取工作流程目錄 + workflows_dir = Path(__file__).parent.parent / ".github" / "workflows" + + if not workflows_dir.exists(): + print(f"❌ 工作流程目錄不存在: {workflows_dir}") + sys.exit(1) + + # 查找所有工作流程文件 + workflow_files = list(workflows_dir.glob("*.yml")) + list( + workflows_dir.glob("*.yaml") + ) + + if not workflow_files: + print(f"❌ 在 {workflows_dir} 中沒有找到工作流程文件") + sys.exit(1) + + print(f"📁 找到 {len(workflow_files)} 個工作流程文件") + print() + + # 驗證每個文件 + all_valid = True + + for workflow_file in sorted(workflow_files): + print(f"🔍 驗證 {workflow_file.name}...") + + # 基本語法驗證 + if not validate_yaml_syntax(workflow_file): + all_valid = False + continue + + # 結構驗證 + if not validate_workflow_structure(workflow_file): + all_valid = False + continue + + # 特定工作流程驗證 + if workflow_file.name == "build-desktop.yml": + if not validate_build_desktop_workflow(workflow_file): + all_valid = False + elif workflow_file.name == "publish.yml": + if not validate_publish_workflow(workflow_file): + all_valid = False + + print() + + # 總結 + if all_valid: + print("🎉 所有工作流程文件驗證通過!") + print() + print("📋 下一步:") + print(" 1. 提交並推送更改到 GitHub") + print(" 2. 測試 'Build Desktop Applications' 工作流程") + print(" 3. 測試 'Build Desktop & Release' 工作流程") + print(" 4. 驗證桌面應用是否正確包含在發佈中") + else: + print("❌ 部分工作流程文件驗證失敗") + print("請修復上述問題後重新運行驗證") + sys.exit(1) + + +if __name__ == "__main__": + main()