fix: macOS 打包步骤添加 25 分钟超时限制 #54
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build & Package | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| platform: | |
| description: "Target platform" | |
| required: true | |
| default: "all" | |
| type: choice | |
| options: | |
| - all | |
| - windows | |
| - macos | |
| - linux | |
| push: | |
| tags: | |
| - "v*" | |
| permissions: | |
| contents: write | |
| jobs: | |
| build-windows: | |
| if: ${{ github.event_name == 'push' || inputs.platform == 'all' || inputs.platform == 'windows' }} | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| cache: npm | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Build Electron app | |
| run: npm run electron:build | |
| - name: Package for Windows | |
| run: npx electron-builder --win --config electron-builder.yml --publish never | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: build-windows | |
| path: | | |
| release/*.exe | |
| release/*.exe.blockmap | |
| release/latest.yml | |
| retention-days: 3 | |
| build-macos: | |
| if: ${{ github.event_name == 'push' || inputs.platform == 'all' || inputs.platform == 'macos' }} | |
| runs-on: macos-latest | |
| env: | |
| HAS_CERT: ${{ secrets.MAC_CERT_P12_BASE64 != '' && 'true' || 'false' }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| cache: npm | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Build Electron app | |
| run: npm run electron:build | |
| - name: Decode signing certificate | |
| if: ${{ env.HAS_CERT == 'true' }} | |
| env: | |
| MAC_CERT_P12_BASE64: ${{ secrets.MAC_CERT_P12_BASE64 }} | |
| MAC_CERT_PASSWORD: ${{ secrets.MAC_CERT_PASSWORD }} | |
| run: | | |
| printf '%s' "$MAC_CERT_P12_BASE64" | tr -d '\r\n' | base64 --decode > /tmp/cert.p12 | |
| openssl pkcs12 -in /tmp/cert.p12 -passin pass:"$MAC_CERT_PASSWORD" -noout | |
| - name: Validate notarization credentials | |
| if: ${{ env.HAS_CERT == 'true' }} | |
| env: | |
| APPLE_ID: ${{ secrets.APPLE_ID }} | |
| APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} | |
| APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
| run: | | |
| echo "APPLE_ID len=${#APPLE_ID}, TEAM=$APPLE_TEAM_ID, APP_PWD len=${#APPLE_APP_SPECIFIC_PASSWORD}" | |
| xcrun notarytool history \ | |
| --apple-id "$APPLE_ID" \ | |
| --password "$APPLE_APP_SPECIFIC_PASSWORD" \ | |
| --team-id "$APPLE_TEAM_ID" >/dev/null | |
| - name: Package for macOS (x64 + arm64) | |
| timeout-minutes: 25 | |
| env: | |
| CSC_LINK: ${{ env.HAS_CERT == 'true' && '/tmp/cert.p12' || '' }} | |
| CSC_KEY_PASSWORD: ${{ secrets.MAC_CERT_PASSWORD }} | |
| APPLE_ID: ${{ secrets.APPLE_ID }} | |
| APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} | |
| APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
| DEBUG: electron-notarize* | |
| run: | | |
| set -euo pipefail | |
| for i in 1 2 3; do | |
| echo "electron-builder attempt $i/3" | |
| if npx electron-builder --mac --config electron-builder.yml --publish never; then | |
| exit 0 | |
| fi | |
| if [ "$i" -lt 3 ]; then | |
| sleep $((i * 60)) | |
| fi | |
| done | |
| exit 1 | |
| - name: Verify code signature | |
| if: ${{ env.HAS_CERT == 'true' }} | |
| run: | | |
| # Verify every .app bundle produced (arm64 + x64) | |
| APPS=$(find release -name "CodePilot.app" -maxdepth 3) | |
| if [ -z "$APPS" ]; then | |
| echo "::warning::No CodePilot.app found, skipping signature verification" | |
| exit 0 | |
| fi | |
| FAILED=0 | |
| while IFS= read -r APP_PATH; do | |
| echo "========== Verifying: $APP_PATH ==========" | |
| # Display signature details | |
| codesign -dv --verbose=4 "$APP_PATH" 2>&1 | tee /tmp/codesign-info.txt || true | |
| # Assert Developer ID authority | |
| if ! grep -q 'Authority=Developer ID Application' /tmp/codesign-info.txt; then | |
| echo "::error::$APP_PATH is NOT signed with Developer ID Application" | |
| FAILED=1 | |
| continue | |
| fi | |
| # Assert correct Team Identifier | |
| if ! grep -q 'TeamIdentifier=K9X599X9Q2' /tmp/codesign-info.txt; then | |
| echo "::error::$APP_PATH has unexpected TeamIdentifier" | |
| FAILED=1 | |
| continue | |
| fi | |
| # Strict verification (must pass) | |
| codesign --verify --deep --strict --verbose=4 "$APP_PATH" | |
| # Gatekeeper assessment (best-effort, does not fail the build) | |
| spctl -a -vvv --type execute "$APP_PATH" || echo "::warning::spctl assessment did not pass for $APP_PATH (may be expected in CI)" | |
| echo "✓ $APP_PATH passed all checks" | |
| done <<< "$APPS" | |
| if [ "$FAILED" -ne 0 ]; then | |
| echo "::error::One or more .app bundles failed signature verification" | |
| exit 1 | |
| fi | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: build-macos | |
| path: | | |
| release/*.dmg | |
| release/*.dmg.blockmap | |
| release/*.zip | |
| release/*.zip.blockmap | |
| release/latest-mac.yml | |
| retention-days: 3 | |
| build-linux: | |
| if: ${{ github.event_name == 'push' || inputs.platform == 'all' || inputs.platform == 'linux' }} | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| arch: [x64, arm64] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| cache: npm | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Build Electron app | |
| run: npm run electron:build | |
| - name: Package for Linux (${{ matrix.arch }}) | |
| run: npx electron-builder --linux --${{ matrix.arch }} --config electron-builder.yml --publish never | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: build-linux-${{ matrix.arch }} | |
| path: | | |
| release/*.AppImage | |
| release/*.deb | |
| release/*.rpm | |
| release/latest-linux*.yml | |
| retention-days: 3 | |
| release: | |
| if: ${{ always() && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') && !contains(needs.*.result, 'cancelled') }} | |
| needs: [build-windows, build-macos, build-linux] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| merge-multiple: true | |
| - name: List artifacts | |
| run: find artifacts -type f | sort | |
| - name: Get version and previous tag | |
| id: meta | |
| run: | | |
| VERSION=${GITHUB_REF_NAME#v} | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| PREV_TAG=$(git tag --sort=-creatordate | grep '^v' | sed -n '2p' || echo "") | |
| echo "prev_tag=$PREV_TAG" >> "$GITHUB_OUTPUT" | |
| - name: Generate changelog | |
| id: changelog | |
| run: | | |
| PREV_TAG="${{ steps.meta.outputs.prev_tag }}" | |
| if [ -n "$PREV_TAG" ]; then | |
| RANGE="${PREV_TAG}..HEAD" | |
| else | |
| RANGE="HEAD" | |
| fi | |
| { | |
| echo "changelog<<CHANGELOG_EOF" | |
| git log "$RANGE" --pretty=format:'| `%h` | %s |' | |
| echo "" | |
| echo "CHANGELOG_EOF" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Create release and upload assets | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| VERSION="${{ steps.meta.outputs.version }}" | |
| PREV_TAG="${{ steps.meta.outputs.prev_tag }}" | |
| cat > release-notes.md << 'NOTES_EOF' | |
| ## Downloads | |
| **Windows** | |
| - **CodePilot Setup ${{ steps.meta.outputs.version }}.exe** — Windows installer (x64 + arm64) | |
| **macOS** | |
| - **CodePilot-${{ steps.meta.outputs.version }}-arm64.dmg** — macOS Apple Silicon (M1/M2/M3/M4) | |
| - **CodePilot-${{ steps.meta.outputs.version }}-x64.dmg** — macOS Intel | |
| **Linux** | |
| - **AppImage / deb / rpm** — x64 and arm64 | |
| ## Installation | |
| **Windows**: Download the exe installer, double-click to install. | |
| **macOS**: | |
| 1. Download the DMG for your chip architecture | |
| 2. Open the DMG, drag CodePilot into Applications | |
| 3. If you see a security prompt on first launch, go to **System Settings > Privacy & Security** and click "Open Anyway" | |
| 4. Configure your Anthropic API Key in Settings | |
| **Linux**: Run the AppImage directly, or install via deb/rpm package. | |
| ## Requirements | |
| - Windows 10+ / macOS 12.0+ / Linux (glibc 2.31+) | |
| - Anthropic API Key or `ANTHROPIC_API_KEY` environment variable | |
| - Claude Code CLI recommended for code features | |
| ## Changelog | |
| NOTES_EOF | |
| if [ -n "$PREV_TAG" ]; then | |
| echo "" >> release-notes.md | |
| echo "| Commit | Description |" >> release-notes.md | |
| echo "|--------|-------------|" >> release-notes.md | |
| cat >> release-notes.md << 'CHANGELOG_INLINE_EOF' | |
| ${{ steps.changelog.outputs.changelog }} | |
| CHANGELOG_INLINE_EOF | |
| fi | |
| # Collect all release files (handle filenames with spaces) | |
| FILES=() | |
| while IFS= read -r f; do | |
| FILES+=("$f") | |
| done < <(find artifacts -type f \( -name "*.dmg" -o -name "*.zip" -o -name "*.exe" -o -name "*.AppImage" -o -name "*.deb" -o -name "*.rpm" -o -name "*.blockmap" -o -name "*.yml" \) | sort) | |
| # Create release and upload all assets in one shot | |
| gh release create "${GITHUB_REF_NAME}" \ | |
| --title "CodePilot v${VERSION}" \ | |
| --notes-file release-notes.md \ | |
| --latest \ | |
| "${FILES[@]}" |