5353 - os : macos-latest
5454 target : aarch64-apple-darwin
5555 artifact : macos-arm
56- - os : macos-15
56+ - os : macos-13
5757 target : x86_64-apple-darwin
5858 artifact : macos-intel
5959
@@ -89,22 +89,39 @@ jobs:
8989
9090 - run : pnpm install --frozen-lockfile
9191
92- # Import Apple certificate for macOS signing
92+ # Import Apple certificate for macOS signing (only when Developer ID is available)
9393 - name : Import Apple certificate
9494 if : matrix.os == 'macos-latest' || matrix.os == 'macos-15'
9595 env :
9696 APPLE_CERTIFICATE : ${{ secrets.APPLE_CERTIFICATE }}
9797 APPLE_CERTIFICATE_PASSWORD : ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
9898 KEYCHAIN_PASSWORD : ${{ secrets.KEYCHAIN_PASSWORD }}
9999 run : |
100- if [ -n "$APPLE_CERTIFICATE" ]; then
101- echo $APPLE_CERTIFICATE | base64 --decode > certificate.p12
102- security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
103- security default-keychain -s build.keychain
104- security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
105- security import certificate.p12 -k build.keychain -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
106- security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" build.keychain
107- rm certificate.p12
100+ if [ -z "$APPLE_CERTIFICATE" ] || [ -z "$KEYCHAIN_PASSWORD" ]; then
101+ echo "No Apple certificate configured — skipping import"
102+ exit 0
103+ fi
104+ echo "$APPLE_CERTIFICATE" | base64 --decode > certificate.p12
105+ security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
106+ security default-keychain -s build.keychain
107+ security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
108+ security import certificate.p12 -k build.keychain -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
109+ security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" build.keychain
110+ rm certificate.p12
111+
112+ # Determine macOS signing identity: Developer ID if available, ad-hoc (-) otherwise
113+ - name : Resolve macOS signing identity
114+ if : matrix.os == 'macos-latest' || matrix.os == 'macos-15'
115+ id : macos-signing
116+ env :
117+ APPLE_SIGNING_IDENTITY : ${{ secrets.APPLE_SIGNING_IDENTITY }}
118+ run : |
119+ if [ -n "$APPLE_SIGNING_IDENTITY" ]; then
120+ echo "identity=$APPLE_SIGNING_IDENTITY" >> "$GITHUB_OUTPUT"
121+ echo "Using Developer ID signing"
122+ else
123+ echo "identity=-" >> "$GITHUB_OUTPUT"
124+ echo "Using ad-hoc signing (no Apple Developer ID)"
108125 fi
109126
110127 - name : Build Tauri app
@@ -114,11 +131,11 @@ jobs:
114131 TAURI_SIGNING_PRIVATE_KEY : ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
115132 TAURI_SIGNING_PRIVATE_KEY_PASSWORD : ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
116133 PKG_CONFIG_PATH : /usr/lib/x86_64-linux-gnu/pkgconfig:/usr/lib/pkgconfig:/usr/share/pkgconfig
117- # macOS signing
118- APPLE_SIGNING_IDENTITY : ${{ secrets.APPLE_SIGNING_IDENTITY }}
134+ # macOS signing — falls back to ad-hoc (-) when no Apple Developer cert
135+ APPLE_SIGNING_IDENTITY : ${{ steps.macos-signing.outputs.identity }}
119136 APPLE_CERTIFICATE : ${{ secrets.APPLE_CERTIFICATE }}
120137 APPLE_CERTIFICATE_PASSWORD : ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
121- # macOS notarization
138+ # macOS notarization (only works with Developer ID)
122139 APPLE_ID : ${{ secrets.APPLE_ID }}
123140 APPLE_PASSWORD : ${{ secrets.APPLE_PASSWORD }}
124141 APPLE_TEAM_ID : ${{ secrets.APPLE_TEAM_ID }}
@@ -131,3 +148,100 @@ jobs:
131148 releaseDraft : false
132149 prerelease : false
133150 updaterJsonKeepUniversal : true
151+
152+ # ─────────────────────────────────────────────────────────────
153+ # Update Homebrew Tap: Push new version to homebrew-mcpmux
154+ # ─────────────────────────────────────────────────────────────
155+ update-homebrew :
156+ needs : [release-please, build-release]
157+ if : needs.release-please.outputs.release_created == 'true'
158+ runs-on : ubuntu-latest
159+ permissions :
160+ contents : read
161+ steps :
162+ - name : Wait for release assets
163+ env :
164+ GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
165+ run : |
166+ VERSION="${{ needs.release-please.outputs.version }}"
167+ TAG="${{ needs.release-please.outputs.tag_name }}"
168+ echo "Checking release assets for $TAG..."
169+
170+ # Wait up to 5 minutes for macOS DMGs to appear
171+ for i in $(seq 1 30); do
172+ ASSETS=$(gh api repos/${{ github.repository }}/releases/tags/$TAG --jq '.assets[].name' 2>/dev/null || echo "")
173+ if echo "$ASSETS" | grep -q "aarch64.dmg" && echo "$ASSETS" | grep -q "x64.dmg"; then
174+ echo "Both macOS DMGs found"
175+ break
176+ fi
177+ echo "Waiting for macOS DMGs... (attempt $i/30)"
178+ sleep 10
179+ done
180+
181+ - name : Compute SHA256 and update cask
182+ env :
183+ GH_TOKEN : ${{ secrets.HOMEBREW_TAP_TOKEN }}
184+ run : |
185+ VERSION="${{ needs.release-please.outputs.version }}"
186+ BASE_URL="https://github.com/${{ github.repository }}/releases/download/v${VERSION}"
187+
188+ # Download DMGs and compute SHA256
189+ echo "Downloading macOS DMGs..."
190+ curl -fSL "${BASE_URL}/McpMux_${VERSION}_aarch64.dmg" -o arm64.dmg || { echo "ARM64 DMG not found — skipping Homebrew update"; exit 0; }
191+ curl -fSL "${BASE_URL}/McpMux_${VERSION}_x64.dmg" -o x64.dmg || { echo "x64 DMG not found — skipping Homebrew update"; exit 0; }
192+
193+ SHA_ARM64=$(shasum -a 256 arm64.dmg | cut -d' ' -f1)
194+ SHA_X64=$(shasum -a 256 x64.dmg | cut -d' ' -f1)
195+ echo "ARM64 SHA256: $SHA_ARM64"
196+ echo "x64 SHA256: $SHA_X64"
197+
198+ # Clone the tap repo and update the cask
199+ git clone https://x-access-token:${GH_TOKEN}@github.com/ion-ash/homebrew-mcpmux.git tap
200+
201+ # Generate cask file (Ruby heredoc content with shell variable expansion)
202+ CASK_FILE="tap/Casks/mcpmux.rb"
203+ {
204+ echo 'cask "mcpmux" do'
205+ echo ' arch arm: "aarch64", intel: "x64"'
206+ echo ''
207+ echo " version \"${VERSION}\""
208+ echo " sha256 arm: \"${SHA_ARM64}\","
209+ echo " intel: \"${SHA_X64}\""
210+ echo ''
211+ echo ' url "https://github.com/ion-ash/mcp-mux/releases/download/v#{version}/McpMux_#{version}_#{arch}.dmg",'
212+ echo ' verified: "github.com/ion-ash/mcp-mux/"'
213+ echo ''
214+ echo ' name "McpMux"'
215+ echo ' desc "Unified MCP gateway and manager for AI clients"'
216+ echo ' homepage "https://mcpmux.com"'
217+ echo ''
218+ echo ' depends_on macos: ">= :high_sierra"'
219+ echo ''
220+ echo ' livecheck do'
221+ echo ' url "https://github.com/ion-ash/mcp-mux/releases/latest"'
222+ echo ' strategy :github_latest'
223+ echo ' end'
224+ echo ''
225+ echo ' app "McpMux.app"'
226+ echo ''
227+ echo ' # Remove quarantine for ad-hoc signed app (no Apple Developer ID)'
228+ echo ' postflight do'
229+ echo ' system_command "/usr/bin/xattr",'
230+ echo ' args: ["-cr", "#{appdir}/McpMux.app"]'
231+ echo ' end'
232+ echo ''
233+ echo ' zap trash: ['
234+ echo ' "~/Library/Application Support/com.mcpmux.desktop",'
235+ echo ' "~/Library/Preferences/com.mcpmux.desktop.plist",'
236+ echo ' "~/Library/Caches/com.mcpmux.desktop",'
237+ echo ' "~/Library/Saved Application State/com.mcpmux.desktop.savedState",'
238+ echo ' ]'
239+ echo 'end'
240+ } > "$CASK_FILE"
241+
242+ cd tap
243+ git config user.name "github-actions[bot]"
244+ git config user.email "github-actions[bot]@users.noreply.github.com"
245+ git add Casks/mcpmux.rb
246+ git commit -m "Update mcpmux to ${VERSION}"
247+ git push
0 commit comments