Release v1.8.249 #323
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 and Release (Code Signed) | |
| on: | |
| push: | |
| tags: | |
| - 'v*' # Triggers on version tags like v1.0.0 | |
| workflow_dispatch: # Allows manual trigger | |
| permissions: | |
| contents: write | |
| jobs: | |
| get-cliproxy-version: | |
| name: Get CLIProxyAPI Version | |
| runs-on: ubuntu-latest | |
| outputs: | |
| version: ${{ steps.get_version.outputs.version }} | |
| tag: ${{ steps.get_version.outputs.tag }} | |
| steps: | |
| - name: Get latest CLIProxyAPI release | |
| id: get_version | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| # Use gh CLI with authentication to avoid rate limiting | |
| LATEST_TAG=$(gh release view --repo router-for-me/CLIProxyAPI --json tagName -q '.tagName') | |
| VERSION=${LATEST_TAG#v} | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| echo "tag=$LATEST_TAG" >> $GITHUB_OUTPUT | |
| echo "Found CLIProxyAPI version: $VERSION (tag: $LATEST_TAG)" | |
| build-signed: | |
| name: Build and Sign macOS App (${{ matrix.arch }}) | |
| needs: get-cliproxy-version | |
| runs-on: macos-14 # macOS Sonoma on Apple Silicon (M1) | |
| strategy: | |
| matrix: | |
| include: | |
| - arch: arm64 | |
| cliproxy_asset_regex: darwin_(aarch64|arm64) | |
| swift_arch: arm64 | |
| tested: true | |
| - arch: x86_64 | |
| cliproxy_asset_regex: darwin_amd64 | |
| swift_arch: x86_64 | |
| tested: false | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Set up Swift | |
| uses: swift-actions/setup-swift@v2 | |
| with: | |
| swift-version: "5.9" | |
| - name: Import Code Signing Certificates | |
| uses: apple-actions/import-codesign-certs@v2 | |
| with: | |
| p12-file-base64: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_P12_BASE64 }} | |
| p12-password: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_PASSWORD }} | |
| - name: Get version from tag | |
| id: get_version | |
| run: | | |
| VERSION=${GITHUB_REF#refs/tags/v} | |
| BUILD_NUMBER=$(git rev-list --count HEAD) | |
| echo "VERSION=${VERSION}" >> $GITHUB_OUTPUT | |
| echo "BUILD_NUMBER=${BUILD_NUMBER}" >> $GITHUB_OUTPUT | |
| echo "Building version: ${VERSION} (build ${BUILD_NUMBER}) for ${{ matrix.arch }}" | |
| - name: Download CLIProxyAPI for ${{ matrix.arch }} | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| set -e | |
| # Use version from pre-fetch job | |
| VERSION="${{ needs.get-cliproxy-version.outputs.version }}" | |
| LATEST_TAG="${{ needs.get-cliproxy-version.outputs.tag }}" | |
| echo "Using CLIProxyAPI version: $VERSION (tag: $LATEST_TAG)" | |
| RELEASE_JSON=$(gh api "repos/router-for-me/CLIProxyAPI/releases/tags/$LATEST_TAG") | |
| ASSET_REGEX="^CLIProxyAPI_.+_${{ matrix.cliproxy_asset_regex }}\\.tar\\.gz$" | |
| FILENAME=$(echo "$RELEASE_JSON" | jq -r --arg regex "$ASSET_REGEX" '.assets[] | select(.name | test($regex)) | .name' | head -n 1) | |
| URL=$(echo "$RELEASE_JSON" | jq -r --arg regex "$ASSET_REGEX" '.assets[] | select(.name | test($regex)) | .browser_download_url' | head -n 1) | |
| if [ -z "$FILENAME" ] || [ "$FILENAME" = "null" ] || [ -z "$URL" ] || [ "$URL" = "null" ]; then | |
| echo "Error: Failed to resolve CLIProxyAPI asset for ${{ matrix.arch }}" >&2 | |
| echo "$RELEASE_JSON" | jq -r '.assets[].name' >&2 | |
| exit 1 | |
| fi | |
| echo "Downloading: $URL" | |
| curl -L -f --retry 3 --retry-delay 5 -o cliproxy.tar.gz "$URL" | |
| # Extract to temp dir and find binary | |
| mkdir -p src/Sources/Resources | |
| TEMP_DIR=$(mktemp -d) | |
| tar -xzf cliproxy.tar.gz -C "$TEMP_DIR" | |
| # Find the binary by expected CLIProxyAPI names. | |
| BINARY=$(find "$TEMP_DIR" -type f \( -name "CLIProxyAPI" -o -name "cli-proxy-api" -o -name "CLIProxyAPIPlus" -o -name "cli-proxy-api-plus" \) | head -1) | |
| if [ -z "$BINARY" ]; then | |
| # Fallback: find any executable that's not a text file | |
| BINARY=$(find "$TEMP_DIR" -type f -perm +111 | head -1) | |
| fi | |
| if [ -z "$BINARY" ]; then | |
| echo "Error: Could not find binary in tarball" >&2 | |
| ls -la "$TEMP_DIR" | |
| exit 1 | |
| fi | |
| echo "Found binary: $BINARY" | |
| cp "$BINARY" src/Sources/Resources/cli-proxy-api-plus | |
| chmod +x src/Sources/Resources/cli-proxy-api-plus | |
| rm -rf "$TEMP_DIR" | |
| # Verify | |
| file src/Sources/Resources/cli-proxy-api-plus | |
| ls -lh src/Sources/Resources/cli-proxy-api-plus | |
| - name: Build app bundle for ${{ matrix.arch }} | |
| env: | |
| CODESIGN_IDENTITY: ${{ secrets.APPLE_DEVELOPER_ID_APPLICATION }} | |
| APP_VERSION: ${{ steps.get_version.outputs.VERSION }} | |
| TARGET_ARCH: ${{ matrix.swift_arch }} | |
| run: | | |
| chmod +x create-app-bundle.sh | |
| ./create-app-bundle.sh | |
| - name: Verify Code Signature | |
| run: | | |
| codesign --verify --deep --strict --verbose=2 VibeProxy.app | |
| codesign -dv --verbose=4 VibeProxy.app | |
| spctl -a -vvv -t install VibeProxy.app || echo "⚠️ Gatekeeper check failed (expected without notarization)" | |
| - name: Notarize App | |
| run: | | |
| ditto -c -k --sequesterRsrc --keepParent "VibeProxy.app" "VibeProxy-notarize.zip" | |
| echo "Submitting app for notarization..." | |
| SUBMIT_OUTPUT=$(xcrun notarytool submit "VibeProxy-notarize.zip" \ | |
| --apple-id "${{ secrets.APPLE_ID }}" \ | |
| --team-id "${{ secrets.APPLE_TEAM_ID }}" \ | |
| --password "${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}" \ | |
| --wait --output-format json) | |
| echo "$SUBMIT_OUTPUT" | |
| SUBMISSION_ID=$(echo "$SUBMIT_OUTPUT" | jq -r '.id') | |
| STATUS=$(echo "$SUBMIT_OUTPUT" | jq -r '.status') | |
| if [ "$STATUS" != "Accepted" ]; then | |
| echo "❌ Notarization failed with status: $STATUS" | |
| echo "📋 Getting detailed log..." | |
| xcrun notarytool log "$SUBMISSION_ID" \ | |
| --apple-id "${{ secrets.APPLE_ID }}" \ | |
| --team-id "${{ secrets.APPLE_TEAM_ID }}" \ | |
| --password "${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}" | |
| exit 1 | |
| fi | |
| echo "✅ Notarization succeeded! Stapling ticket..." | |
| xcrun stapler staple "VibeProxy.app" | |
| xcrun stapler validate "VibeProxy.app" | |
| echo "✅ App successfully notarized and stapled!" | |
| rm "VibeProxy-notarize.zip" | |
| - name: Create DMG | |
| run: | | |
| brew install create-dmg | |
| create-dmg \ | |
| --volname "VibeProxy" \ | |
| --volicon "icon.png" \ | |
| --window-pos 200 120 \ | |
| --window-size 600 400 \ | |
| --icon-size 100 \ | |
| --icon "VibeProxy.app" 175 120 \ | |
| --hide-extension "VibeProxy.app" \ | |
| --app-drop-link 425 120 \ | |
| --codesign "${{ secrets.APPLE_DEVELOPER_ID_APPLICATION }}" \ | |
| "VibeProxy-${{ matrix.arch }}.dmg" \ | |
| "VibeProxy.app" || true | |
| if [ -f "VibeProxy-${{ matrix.arch }}.dmg" ]; then | |
| echo "✅ DMG created successfully" | |
| codesign -dv "VibeProxy-${{ matrix.arch }}.dmg" || echo "⚠️ DMG not signed (non-fatal)" | |
| fi | |
| - name: Create ZIP archive | |
| run: | | |
| ditto -c -k --sequesterRsrc --keepParent "VibeProxy.app" "VibeProxy-${{ matrix.arch }}.zip" | |
| - name: Calculate checksums | |
| run: | | |
| shasum -a 256 VibeProxy-${{ matrix.arch }}.zip > VibeProxy-${{ matrix.arch }}.zip.sha256 | |
| if [ -f "VibeProxy-${{ matrix.arch }}.dmg" ]; then | |
| shasum -a 256 VibeProxy-${{ matrix.arch }}.dmg > VibeProxy-${{ matrix.arch }}.dmg.sha256 | |
| fi | |
| - name: Sign update for Sparkle | |
| id: sparkle_sign | |
| env: | |
| SPARKLE_PRIVATE_KEY: ${{ secrets.SPARKLE_PRIVATE_KEY }} | |
| run: | | |
| SIGN_UPDATE=$(find src/.build -name "sign_update" -type f | grep -v old_dsa | head -1) | |
| if [ -z "$SIGN_UPDATE" ]; then | |
| echo "Error: sign_update not found" | |
| find src/.build -name "sign_update" -type f | |
| exit 1 | |
| fi | |
| echo "Using sign_update: $SIGN_UPDATE" | |
| SIGNATURE=$(echo "$SPARKLE_PRIVATE_KEY" | "$SIGN_UPDATE" VibeProxy-${{ matrix.arch }}.zip --ed-key-file -) | |
| ED_SIGNATURE=$(echo "$SIGNATURE" | grep -o 'sparkle:edSignature="[^"]*"' | sed 's/sparkle:edSignature="//;s/"$//') | |
| LENGTH=$(stat -f%z VibeProxy-${{ matrix.arch }}.zip) | |
| echo "ed_signature=$ED_SIGNATURE" >> $GITHUB_OUTPUT | |
| echo "length=$LENGTH" >> $GITHUB_OUTPUT | |
| echo "Sparkle signature generated for ${{ matrix.arch }}" | |
| - name: Upload build artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: vibeproxy-${{ matrix.arch }} | |
| path: | | |
| VibeProxy-${{ matrix.arch }}.zip | |
| VibeProxy-${{ matrix.arch }}.zip.sha256 | |
| VibeProxy-${{ matrix.arch }}.dmg | |
| VibeProxy-${{ matrix.arch }}.dmg.sha256 | |
| retention-days: 1 | |
| - name: Save Sparkle signature | |
| run: | | |
| echo "${{ steps.sparkle_sign.outputs.ed_signature }}" > sparkle_signature_${{ matrix.arch }}.txt | |
| echo "${{ steps.sparkle_sign.outputs.length }}" > sparkle_length_${{ matrix.arch }}.txt | |
| - name: Upload Sparkle signature | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: sparkle-signature-${{ matrix.arch }} | |
| path: | | |
| sparkle_signature_${{ matrix.arch }}.txt | |
| sparkle_length_${{ matrix.arch }}.txt | |
| retention-days: 1 | |
| release: | |
| name: Create Release | |
| needs: [get-cliproxy-version, build-signed] | |
| runs-on: macos-14 | |
| if: startsWith(github.ref, 'refs/tags/') | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Get version from tag | |
| id: get_version | |
| run: | | |
| VERSION=${GITHUB_REF#refs/tags/v} | |
| echo "VERSION=${VERSION}" >> $GITHUB_OUTPUT | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| - name: Prepare release files | |
| run: | | |
| mkdir -p release | |
| cp artifacts/vibeproxy-arm64/* release/ | |
| cp artifacts/vibeproxy-x86_64/* release/ | |
| ls -la release/ | |
| - name: Download Sparkle signatures | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: sparkle-signature-* | |
| path: sparkle | |
| merge-multiple: true | |
| - name: Update appcast files | |
| run: | | |
| VERSION="${{ steps.get_version.outputs.VERSION }}" | |
| BUILD_NUMBER=$(git rev-list --count HEAD) | |
| TODAY=$(date -u +"%a, %d %b %Y %H:%M:%S +0000") | |
| # Read signatures for both architectures | |
| ARM64_SIGNATURE=$(cat sparkle/sparkle_signature_arm64.txt) | |
| ARM64_LENGTH=$(cat sparkle/sparkle_length_arm64.txt) | |
| X86_64_SIGNATURE=$(cat sparkle/sparkle_signature_x86_64.txt) | |
| X86_64_LENGTH=$(cat sparkle/sparkle_length_x86_64.txt) | |
| # Update arm64 appcast (appcast.xml - default) | |
| cat > /tmp/new_item_arm64.xml << ENDOFITEM | |
| <item> | |
| <title>Version ${VERSION}</title> | |
| <sparkle:version>${BUILD_NUMBER}</sparkle:version> | |
| <sparkle:shortVersionString>${VERSION}</sparkle:shortVersionString> | |
| <sparkle:minimumSystemVersion>13.0</sparkle:minimumSystemVersion> | |
| <pubDate>${TODAY}</pubDate> | |
| <enclosure url="https://github.com/automazeio/vibeproxy/releases/download/v${VERSION}/VibeProxy-arm64.zip" | |
| type="application/octet-stream" | |
| sparkle:edSignature="${ARM64_SIGNATURE}" | |
| length="${ARM64_LENGTH}"/> | |
| </item> | |
| ENDOFITEM | |
| sed -i.bak '/<language>en<\/language>/r /tmp/new_item_arm64.xml' appcast.xml | |
| rm -f appcast.xml.bak /tmp/new_item_arm64.xml | |
| echo "Updated appcast.xml (arm64) with version ${VERSION}" | |
| # Create/update x86_64 appcast (appcast-x86_64.xml) | |
| if [ ! -f appcast-x86_64.xml ]; then | |
| # Create new x86_64 appcast from template | |
| cat > appcast-x86_64.xml << 'ENDOFAPPCAST' | |
| <?xml version="1.0" encoding="utf-8"?> | |
| <rss version="2.0" xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" xmlns:dc="http://purl.org/dc/elements/1.1/"> | |
| <channel> | |
| <title>VibeProxy Updates (Intel)</title> | |
| <link>https://github.com/automazeio/vibeproxy</link> | |
| <description>Most recent updates to VibeProxy for Intel Macs</description> | |
| <language>en</language> | |
| </channel> | |
| </rss> | |
| ENDOFAPPCAST | |
| fi | |
| # Add new item to x86_64 appcast | |
| cat > /tmp/new_item_x86_64.xml << ENDOFITEM | |
| <item> | |
| <title>Version ${VERSION}</title> | |
| <sparkle:version>${BUILD_NUMBER}</sparkle:version> | |
| <sparkle:shortVersionString>${VERSION}</sparkle:shortVersionString> | |
| <sparkle:minimumSystemVersion>13.0</sparkle:minimumSystemVersion> | |
| <pubDate>${TODAY}</pubDate> | |
| <enclosure url="https://github.com/automazeio/vibeproxy/releases/download/v${VERSION}/VibeProxy-x86_64.zip" | |
| type="application/octet-stream" | |
| sparkle:edSignature="${X86_64_SIGNATURE}" | |
| length="${X86_64_LENGTH}"/> | |
| </item> | |
| ENDOFITEM | |
| sed -i.bak '/<language>en<\/language>/r /tmp/new_item_x86_64.xml' appcast-x86_64.xml | |
| rm -f appcast-x86_64.xml.bak /tmp/new_item_x86_64.xml | |
| echo "Updated appcast-x86_64.xml with version ${VERSION}" | |
| head -30 appcast.xml | |
| echo "---" | |
| head -30 appcast-x86_64.xml | |
| - name: Commit appcast files | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git fetch origin main | |
| git checkout main | |
| git pull origin main | |
| git add appcast.xml appcast-x86_64.xml | |
| git commit -m "Update appcast files for v${{ steps.get_version.outputs.VERSION }}" || echo "No changes to commit" | |
| git push origin main | |
| - name: Create Release | |
| uses: softprops/action-gh-release@v1 | |
| with: | |
| files: | | |
| release/VibeProxy-arm64.zip | |
| release/VibeProxy-arm64.zip.sha256 | |
| release/VibeProxy-arm64.dmg | |
| release/VibeProxy-arm64.dmg.sha256 | |
| release/VibeProxy-x86_64.zip | |
| release/VibeProxy-x86_64.zip.sha256 | |
| release/VibeProxy-x86_64.dmg | |
| release/VibeProxy-x86_64.dmg.sha256 | |
| draft: false | |
| prerelease: false | |
| generate_release_notes: true | |
| body: | | |
| ## VibeProxy ${{ steps.get_version.outputs.VERSION }} | |
| ### Downloads | |
| | Architecture | DMG | ZIP | | |
| |--------------|-----|-----| | |
| | **Apple Silicon** (M1/M2/M3) | `VibeProxy-arm64.dmg` | `VibeProxy-arm64.zip` | | |
| | **Intel** (x86_64) ⚠️ | `VibeProxy-x86_64.dmg` | `VibeProxy-x86_64.zip` | | |
| > ⚠️ **Intel build is untested** - Please report if it works for you! | |
| ### Installation | |
| 1. Download the appropriate file for your Mac | |
| 2. For DMG: Mount and drag to Applications | |
| 3. For ZIP: Extract and drag to Applications | |
| 4. Double-click to launch | |
| ### What's New | |
| See the [CHANGELOG](https://github.com/automazeio/vibeproxy/blob/main/CHANGELOG.md) for details. | |
| ### Verification | |
| This release is **code signed** with Apple Developer ID and includes SHA-256 checksums. | |
| ```bash | |
| shasum -a 256 -c VibeProxy-arm64.zip.sha256 | |
| ``` | |
| --- | |
| ✅ **Code Signed & Notarized** - No Gatekeeper warnings! | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |