Enhance macOS code signing process in GitHub Actions workflow by addi… #46
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: Release | |
| on: | |
| push: | |
| tags: ["v*"] | |
| permissions: | |
| contents: write | |
| jobs: | |
| build: | |
| strategy: | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| goos: linux | |
| goarch: amd64 | |
| cgo: "0" | |
| name: mcpproxy-linux-amd64 | |
| archive_format: tar.gz | |
| - os: ubuntu-latest | |
| goos: windows | |
| goarch: amd64 | |
| cgo: "0" | |
| name: mcpproxy-windows-amd64.exe | |
| archive_format: zip | |
| - os: macos-latest | |
| goos: darwin | |
| goarch: amd64 | |
| cgo: "1" | |
| name: mcpproxy-darwin-amd64 | |
| archive_format: tar.gz | |
| - os: macos-latest | |
| goos: darwin | |
| goarch: arm64 | |
| cgo: "1" | |
| name: mcpproxy-darwin-arm64 | |
| archive_format: tar.gz | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Set up Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: "1.23.10" | |
| - name: Cache Go modules | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/go/pkg/mod | |
| key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} | |
| restore-keys: | | |
| ${{ runner.os }}-go- | |
| - name: Download dependencies | |
| run: go mod download | |
| - name: Import Code-Signing Certificates (macOS) | |
| if: matrix.goos == 'darwin' | |
| run: | | |
| echo "Converting and importing legacy P12 certificate..." | |
| # Decode the base64 P12 file | |
| echo "${{ secrets.APPLE_DEVELOPER_ID_CERT }}" | base64 -d > legacy_cert.p12 | |
| # Try extracting with standard OpenSSL first | |
| echo "Attempting extraction with standard OpenSSL..." | |
| if openssl pkcs12 -in legacy_cert.p12 \ | |
| -passin pass:"${{ secrets.APPLE_DEVELOPER_ID_CERT_PASSWORD }}" \ | |
| -nodes \ | |
| -out temp_cert_and_key.pem 2>/dev/null; then | |
| echo "✅ Standard OpenSSL extraction succeeded" | |
| else | |
| echo "❌ Standard OpenSSL failed, installing OpenSSL 3.x with legacy provider..." | |
| # Install OpenSSL 3.x with legacy provider support | |
| brew install openssl@3 | |
| export PATH="/opt/homebrew/bin:$PATH" | |
| # Verify OpenSSL version | |
| openssl version | |
| # Extract with legacy provider support | |
| openssl pkcs12 -in legacy_cert.p12 \ | |
| -passin pass:"${{ secrets.APPLE_DEVELOPER_ID_CERT_PASSWORD }}" \ | |
| -provider legacy -provider default \ | |
| -nodes \ | |
| -out temp_cert_and_key.pem | |
| echo "✅ Legacy provider extraction succeeded" | |
| fi | |
| # Verify extraction worked | |
| if [ ! -f temp_cert_and_key.pem ]; then | |
| echo "❌ Certificate extraction failed" | |
| exit 1 | |
| fi | |
| echo "Certificates found: $(grep -c "BEGIN CERTIFICATE" temp_cert_and_key.pem)" | |
| echo "Private keys found: $(grep -c "BEGIN PRIVATE KEY" temp_cert_and_key.pem)" | |
| # Import certificate and key separately to avoid P12 compatibility issues | |
| openssl x509 -in temp_cert_and_key.pem -out cert_only.pem | |
| openssl rsa -in temp_cert_and_key.pem -out key_only.pem | |
| # Try importing to login keychain first, fallback to temp keychain | |
| echo "=== Importing certificates ===" | |
| # Skip login keychain in CI to avoid conflicts between parallel workers | |
| # Go directly to isolated temporary keychain | |
| echo "Creating isolated temporary keychain for this worker..." | |
| # Create unique keychain name for this matrix job to prevent conflicts | |
| UNIQUE_ID="${{ matrix.goos }}-${{ matrix.goarch }}-$$-$(date +%s)" | |
| TEMP_KEYCHAIN="mcpproxy-build-${UNIQUE_ID}.keychain" | |
| echo "Using keychain: ${TEMP_KEYCHAIN}" | |
| # Create isolated temporary keychain | |
| security create-keychain -p "temp123" "$TEMP_KEYCHAIN" | |
| # Add to search list WITHOUT setting as default (avoid conflicts) | |
| security list-keychains -s "$TEMP_KEYCHAIN" ~/Library/Keychains/login.keychain-db /Library/Keychains/System.keychain | |
| # Unlock and configure | |
| security unlock-keychain -p "temp123" "$TEMP_KEYCHAIN" | |
| security set-keychain-settings -t 3600 -l "$TEMP_KEYCHAIN" | |
| # Import to isolated keychain | |
| security import cert_only.pem -k "$TEMP_KEYCHAIN" -T /usr/bin/codesign | |
| security import key_only.pem -k "$TEMP_KEYCHAIN" -T /usr/bin/codesign | |
| # Set partition list for isolated keychain | |
| security set-key-partition-list -S apple-tool:,apple: -s -k "temp123" "$TEMP_KEYCHAIN" | |
| echo "✅ Imported to isolated temporary keychain: ${TEMP_KEYCHAIN}" | |
| # Store keychain name for cleanup | |
| echo "$TEMP_KEYCHAIN" > .keychain_name | |
| # Verify import | |
| echo "=== Available code signing identities ===" | |
| security find-identity -v -p codesigning | |
| # Clean up temporary files | |
| rm -f legacy_cert.p12 temp_cert_and_key.pem cert_only.pem key_only.pem | |
| echo "✅ Certificate import completed" | |
| - name: Build binary and create archives | |
| env: | |
| CGO_ENABLED: ${{ matrix.cgo }} | |
| GOOS: ${{ matrix.goos }} | |
| GOARCH: ${{ matrix.goarch }} | |
| run: | | |
| VERSION=${GITHUB_REF#refs/tags/} | |
| LDFLAGS="-s -w -X mcpproxy-go/cmd/mcpproxy.version=${VERSION} -X main.version=${VERSION}" | |
| # Determine clean binary name | |
| if [ "${{ matrix.goos }}" = "windows" ]; then | |
| CLEAN_BINARY="mcpproxy.exe" | |
| else | |
| CLEAN_BINARY="mcpproxy" | |
| fi | |
| # Create clean binary for archive | |
| go build -ldflags "${LDFLAGS}" -o ${CLEAN_BINARY} ./cmd/mcpproxy | |
| # Code sign macOS binaries | |
| if [ "${{ matrix.goos }}" = "darwin" ]; then | |
| echo "Code signing macOS binary..." | |
| # Debug: List all available certificates | |
| echo "Available certificates:" | |
| security find-identity -v -p codesigning | |
| # Find the Developer ID certificate identity | |
| CERT_IDENTITY=$(security find-identity -v -p codesigning | grep "Developer ID Application" | head -1 | grep -o '"[^"]*"' | tr -d '"') | |
| # Verify we found a valid certificate | |
| if [ -n "${CERT_IDENTITY}" ]; then | |
| echo "✅ Found Developer ID certificate: ${CERT_IDENTITY}" | |
| else | |
| echo "❌ No Developer ID certificate found, using team ID as fallback" | |
| CERT_IDENTITY="${{ secrets.APPLE_TEAM_ID }}" | |
| echo "⚠️ Using fallback identity: ${CERT_IDENTITY}" | |
| fi | |
| # Validate entitlements file formatting (Apple's recommendation) | |
| echo "=== Validating entitlements file ===" | |
| if [ -f "scripts/entitlements.plist" ]; then | |
| echo "Validating entitlements formatting with plutil..." | |
| if plutil -lint scripts/entitlements.plist; then | |
| echo "✅ Entitlements file is properly formatted" | |
| else | |
| echo "❌ Entitlements file has formatting issues" | |
| exit 1 | |
| fi | |
| # Convert to XML format if needed (Apple's recommendation) | |
| plutil -convert xml1 scripts/entitlements.plist | |
| echo "✅ Entitlements converted to XML format" | |
| else | |
| echo "⚠️ No entitlements file found" | |
| fi | |
| # Sign with proper Developer ID certificate, hardened runtime, and timestamp | |
| echo "=== Signing binary with hardened runtime ===" | |
| # Install GNU coreutils for timeout command (macOS compatibility) | |
| if ! command -v timeout &> /dev/null; then | |
| echo "Installing GNU coreutils for timeout command..." | |
| brew install coreutils | |
| # Use gtimeout from coreutils | |
| TIMEOUT_CMD="gtimeout" | |
| else | |
| TIMEOUT_CMD="timeout" | |
| fi | |
| # Add timeout and retry logic for signing to prevent hanging | |
| SIGN_SUCCESS=false | |
| for attempt in 1 2 3; do | |
| echo "Signing attempt $attempt/3..." | |
| # Use timeout command to prevent hanging (max 5 minutes per attempt) | |
| if $TIMEOUT_CMD 300 codesign --force \ | |
| --options runtime \ | |
| --entitlements scripts/entitlements.plist \ | |
| --sign "${CERT_IDENTITY}" \ | |
| --timestamp \ | |
| ${CLEAN_BINARY}; then | |
| SIGN_SUCCESS=true | |
| echo "✅ Signing succeeded on attempt $attempt" | |
| break | |
| else | |
| echo "❌ Signing attempt $attempt failed or timed out" | |
| if [ $attempt -lt 3 ]; then | |
| echo "Retrying in 10 seconds..." | |
| sleep 10 | |
| fi | |
| fi | |
| done | |
| if [ "$SIGN_SUCCESS" != "true" ]; then | |
| echo "❌ All signing attempts failed" | |
| # Try signing without timestamp as fallback | |
| echo "Attempting fallback signing without timestamp..." | |
| if $TIMEOUT_CMD 120 codesign --force \ | |
| --options runtime \ | |
| --entitlements scripts/entitlements.plist \ | |
| --sign "${CERT_IDENTITY}" \ | |
| ${CLEAN_BINARY}; then | |
| echo "⚠️ Fallback signing succeeded (without timestamp)" | |
| echo "NOTE: This binary may not pass notarization without timestamp" | |
| else | |
| echo "❌ Even fallback signing failed - cannot proceed" | |
| exit 1 | |
| fi | |
| fi | |
| # Verify signing, hardened runtime, and timestamp using Apple's recommended methods | |
| echo "=== Verifying binary signature (Apple's recommended verification) ===" | |
| # Basic verification | |
| codesign --verify --verbose ${CLEAN_BINARY} | |
| echo "Basic verification: $?" | |
| # Apple's recommended strict verification for notarization | |
| echo "=== Strict verification (matches notarization requirements) ===" | |
| if codesign -vvv --deep --strict ${CLEAN_BINARY}; then | |
| echo "✅ Strict verification PASSED - ready for notarization" | |
| else | |
| echo "❌ Strict verification FAILED - will not pass notarization" | |
| exit 1 | |
| fi | |
| # Check for secure timestamp (Apple's recommended check) | |
| echo "=== Checking for secure timestamp ===" | |
| TIMESTAMP_CHECK=$(codesign -dvv ${CLEAN_BINARY} 2>&1) | |
| if echo "$TIMESTAMP_CHECK" | grep -q "Timestamp="; then | |
| echo "✅ Secure timestamp present:" | |
| echo "$TIMESTAMP_CHECK" | grep "Timestamp=" | |
| else | |
| echo "❌ No secure timestamp found" | |
| echo "Full output:" | |
| echo "$TIMESTAMP_CHECK" | |
| fi | |
| # Display detailed signature info | |
| codesign --display --verbose=4 ${CLEAN_BINARY} | |
| # Check entitlements formatting (Apple's recommendation) | |
| echo "=== Checking entitlements formatting ===" | |
| codesign --display --entitlements - ${CLEAN_BINARY} | head -10 | |
| # Verify with spctl (Gatekeeper assessment) - expected to fail before notarization | |
| echo "=== Gatekeeper assessment (expected to fail before notarization) ===" | |
| if spctl --assess --verbose ${CLEAN_BINARY}; then | |
| echo "✅ Gatekeeper assessment: PASSED (unexpected but good!)" | |
| else | |
| echo "⚠️ Gatekeeper assessment: REJECTED (expected - binary needs notarization)" | |
| echo "This is normal - the binary will pass after Apple completes notarization" | |
| fi | |
| echo "✅ Binary signed successfully with hardened runtime and timestamp" | |
| fi | |
| # Create archive with version info | |
| ARCHIVE_BASE="mcpproxy-${VERSION#v}-${{ matrix.goos }}-${{ matrix.goarch }}" | |
| LATEST_ARCHIVE_BASE="mcpproxy-latest-${{ matrix.goos }}-${{ matrix.goarch }}" | |
| if [ "${{ matrix.archive_format }}" = "zip" ]; then | |
| # Create versioned archive | |
| zip "${ARCHIVE_BASE}.zip" ${CLEAN_BINARY} | |
| # Create latest archive | |
| zip "${LATEST_ARCHIVE_BASE}.zip" ${CLEAN_BINARY} | |
| else | |
| # Create versioned archive | |
| tar -czf "${ARCHIVE_BASE}.tar.gz" ${CLEAN_BINARY} | |
| # Create latest archive | |
| tar -czf "${LATEST_ARCHIVE_BASE}.tar.gz" ${CLEAN_BINARY} | |
| fi | |
| - name: Create .icns icon (macOS) | |
| if: matrix.goos == 'darwin' | |
| run: | | |
| chmod +x scripts/create-icns.sh | |
| ./scripts/create-icns.sh | |
| - name: Create DMG installer (macOS) | |
| if: matrix.goos == 'darwin' | |
| run: | | |
| VERSION=${GITHUB_REF#refs/tags/} | |
| chmod +x scripts/create-dmg.sh | |
| # Determine binary name | |
| CLEAN_BINARY="mcpproxy" | |
| # Create DMG | |
| ./scripts/create-dmg.sh ${CLEAN_BINARY} ${VERSION} ${{ matrix.goarch }} | |
| # Sign DMG | |
| DMG_NAME="mcpproxy-${VERSION#v}-darwin-${{ matrix.goarch }}.dmg" | |
| echo "Signing DMG: ${DMG_NAME}" | |
| # Find the Developer ID certificate identity | |
| CERT_IDENTITY=$(security find-identity -v -p codesigning | grep "Developer ID Application" | head -1 | grep -o '"[^"]*"' | tr -d '"') | |
| # Verify we found a valid certificate | |
| if [ -n "${CERT_IDENTITY}" ]; then | |
| echo "✅ Found Developer ID certificate for DMG: ${CERT_IDENTITY}" | |
| else | |
| echo "❌ No Developer ID certificate found for DMG, using team ID as fallback" | |
| CERT_IDENTITY="${{ secrets.APPLE_TEAM_ID }}" | |
| echo "⚠️ Using fallback identity for DMG: ${CERT_IDENTITY}" | |
| fi | |
| # Sign DMG with proper certificate and timestamp | |
| codesign --force \ | |
| --sign "${CERT_IDENTITY}" \ | |
| --timestamp \ | |
| "${DMG_NAME}" | |
| # Verify DMG signing | |
| echo "=== Verifying DMG signature ===" | |
| codesign --verify --verbose "${DMG_NAME}" | |
| echo "DMG verification: $?" | |
| codesign --display --verbose=4 "${DMG_NAME}" | |
| echo "✅ DMG created and signed successfully: ${DMG_NAME}" | |
| - name: Submit for notarization (macOS) | |
| if: matrix.goos == 'darwin' | |
| run: | | |
| VERSION=${GITHUB_REF#refs/tags/} | |
| DMG_NAME="mcpproxy-${VERSION#v}-darwin-${{ matrix.goarch }}.dmg" | |
| echo "Submitting DMG for notarization..." | |
| SUBMISSION_ID=$(xcrun notarytool submit "${DMG_NAME}" --apple-id "${{ secrets.APPLE_ID_USERNAME }}" --password "${{ secrets.APPLE_ID_APP_PASSWORD }}" --team-id "${{ secrets.APPLE_TEAM_ID }}" --output-format json | jq -r '.id') | |
| echo "Submission ID: ${SUBMISSION_ID}" | |
| echo "${SUBMISSION_ID}" > "${DMG_NAME}.submission_id" | |
| echo "DMG submitted for notarization (ID: ${SUBMISSION_ID})" | |
| - name: Cleanup isolated keychain (macOS) | |
| if: matrix.goos == 'darwin' && always() | |
| run: | | |
| # Clean up the isolated keychain we created for this worker | |
| if [ -f .keychain_name ]; then | |
| TEMP_KEYCHAIN=$(cat .keychain_name) | |
| echo "Cleaning up keychain: ${TEMP_KEYCHAIN}" | |
| # Remove from search list and delete | |
| security delete-keychain "$TEMP_KEYCHAIN" 2>/dev/null || echo "Keychain already cleaned up" | |
| rm -f .keychain_name | |
| echo "✅ Keychain cleanup completed" | |
| else | |
| echo "No keychain to clean up" | |
| fi | |
| - name: Upload versioned archive artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: versioned-${{ matrix.goos }}-${{ matrix.goarch }} | |
| path: mcpproxy-*-${{ matrix.goos }}-${{ matrix.goarch }}.${{ matrix.archive_format }} | |
| - name: Upload latest archive artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: latest-${{ matrix.goos }}-${{ matrix.goarch }} | |
| path: mcpproxy-latest-${{ matrix.goos }}-${{ matrix.goarch }}.${{ matrix.archive_format }} | |
| - name: Upload DMG installer (macOS) | |
| if: matrix.goos == 'darwin' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: dmg-${{ matrix.goos }}-${{ matrix.goarch }} | |
| path: | | |
| mcpproxy-*-darwin-${{ matrix.goarch }}.dmg | |
| mcpproxy-*-darwin-${{ matrix.goarch }}.dmg.submission_id | |
| release: | |
| needs: build | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: dist | |
| - name: Reorganize files | |
| run: | | |
| VERSION=${GITHUB_REF#refs/tags/} | |
| # Create a flat structure to avoid duplicates | |
| mkdir -p release-files | |
| # Copy archives (tar.gz and zip files) | |
| find dist -name "*.tar.gz" -o -name "*.zip" | while read file; do | |
| filename=$(basename "$file") | |
| cp "$file" "release-files/$filename" | |
| done | |
| # Handle DMG files and notarization submissions | |
| mkdir -p pending-notarizations | |
| find dist -path "*/dmg-*" -name "*.dmg" | while read dmg_file; do | |
| filename=$(basename "$dmg_file") | |
| submission_id_file="${dmg_file}.submission_id" | |
| if [ -f "$submission_id_file" ]; then | |
| # DMG has pending notarization | |
| echo "Found pending notarization for $filename" | |
| cp "$dmg_file" "release-files/$filename" | |
| # Create pending notarization record | |
| SUBMISSION_ID=$(cat "$submission_id_file") | |
| cat > "pending-notarizations/${filename}.pending" << EOF | |
| { | |
| "submission_id": "$SUBMISSION_ID", | |
| "dmg_name": "$filename", | |
| "version": "$VERSION", | |
| "submitted_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)" | |
| } | |
| EOF | |
| else | |
| # No notarization submission (shouldn't happen, but handle it) | |
| echo "No submission ID for $filename, copying as-is" | |
| cp "$dmg_file" "release-files/$filename" | |
| fi | |
| done | |
| - name: List files for upload | |
| run: | | |
| echo "Files to upload:" | |
| ls -la release-files/ | |
| echo "Pending notarizations:" | |
| ls -la pending-notarizations/ || echo "No pending notarizations" | |
| - name: Create release with binaries | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| files: release-files/* | |
| body: | | |
| ## mcpproxy ${{ github.ref_name }} | |
| Smart MCP Proxy - Intelligent tool discovery and proxying for Model Context Protocol servers. | |
| ### Download Links | |
| **Latest Version (auto-updates):** | |
| - [Linux AMD64](https://github.com/${{ github.repository }}/releases/latest/download/mcpproxy-latest-linux-amd64.tar.gz) | |
| - [Windows AMD64](https://github.com/${{ github.repository }}/releases/latest/download/mcpproxy-latest-windows-amd64.zip) | |
| - [macOS AMD64](https://github.com/${{ github.repository }}/releases/latest/download/mcpproxy-latest-darwin-amd64.tar.gz) | |
| - [macOS ARM64](https://github.com/${{ github.repository }}/releases/latest/download/mcpproxy-latest-darwin-arm64.tar.gz) | |
| **macOS Installers (Recommended):** | |
| - [macOS Universal DMG](https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/mcpproxy-${{ github.ref_name }}-darwin-arm64.dmg) (Apple Silicon) | |
| - [macOS Intel DMG](https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/mcpproxy-${{ github.ref_name }}-darwin-amd64.dmg) (Intel) | |
| **This Version (${{ github.ref_name }}):** | |
| - [Linux AMD64](https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/mcpproxy-${{ github.ref_name }}-linux-amd64.tar.gz) | |
| - [Windows AMD64](https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/mcpproxy-${{ github.ref_name }}-windows-amd64.zip) | |
| - [macOS AMD64](https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/mcpproxy-${{ github.ref_name }}-darwin-amd64.tar.gz) | |
| - [macOS ARM64](https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/mcpproxy-${{ github.ref_name }}-darwin-arm64.tar.gz) | |
| ### Installation | |
| **macOS (Recommended - DMG Installer):** | |
| 1. Download the appropriate DMG file for your Mac (Apple Silicon or Intel) | |
| 2. Double-click the DMG to mount it | |
| 3. Drag mcpproxy.app to Applications folder | |
| 4. Launch from Applications or Launchpad | |
| 5. The app will appear in your system tray with autostart capability | |
| **Manual Installation (All Platforms):** | |
| 1. Download the appropriate archive for your platform using the links above | |
| 2. Extract the archive: `tar -xzf mcpproxy-*.tar.gz` (Linux/macOS) or unzip (Windows) | |
| 3. Make it executable: `chmod +x mcpproxy` (Linux/macOS) | |
| 4. Run `./mcpproxy` to start | |
| ### Platform Support | |
| - **macOS**: Full system tray support with menu and icons | |
| - **Windows**: Full system tray support with menu and icons | |
| - **Linux**: Headless mode only (no system tray due to compatibility) | |
| ### Usage | |
| - With tray: `./mcpproxy` (default) | |
| - Headless: `./mcpproxy --tray=false` | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Upload pending notarizations | |
| if: hashFiles('pending-notarizations/*.pending') != '' | |
| run: | | |
| # Upload pending notarization files as release assets | |
| for pending_file in pending-notarizations/*.pending; do | |
| if [ -f "$pending_file" ]; then | |
| echo "Uploading pending notarization: $(basename "$pending_file")" | |
| gh release upload "${{ github.ref_name }}" "$pending_file" --clobber | |
| fi | |
| done | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| update-homebrew: | |
| needs: release | |
| runs-on: ubuntu-latest | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| steps: | |
| - name: Checkout tap repository | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: smart-mcp-proxy/homebrew-mcpproxy | |
| token: ${{ secrets.HOMEBREW_TAP_TOKEN }} | |
| path: tap | |
| - name: Update Homebrew formula | |
| run: | | |
| VERSION=${GITHUB_REF#refs/tags/v} | |
| echo "Processing version: ${VERSION}" | |
| cd tap | |
| # Add debugging and retry logic | |
| ARCHIVE_URL="https://github.com/${{ github.repository }}/archive/refs/tags/v${VERSION}.tar.gz" | |
| echo "Downloading from: ${ARCHIVE_URL}" | |
| # Wait a bit for GitHub to generate the archive | |
| sleep 10 | |
| # Try downloading with retries | |
| for i in {1..5}; do | |
| echo "Download attempt ${i}/5..." | |
| if wget -q "${ARCHIVE_URL}" -O "v${VERSION}.tar.gz"; then | |
| echo "Download successful" | |
| break | |
| else | |
| echo "Download failed, retrying in 10 seconds..." | |
| sleep 10 | |
| fi | |
| if [ $i -eq 5 ]; then | |
| echo "All download attempts failed" | |
| echo "Checking if file exists at URL..." | |
| curl -I "${ARCHIVE_URL}" || true | |
| exit 1 | |
| fi | |
| done | |
| # Verify file was downloaded | |
| if [ ! -f "v${VERSION}.tar.gz" ]; then | |
| echo "Archive file not found after download" | |
| ls -la | |
| exit 1 | |
| fi | |
| # Calculate SHA256 | |
| SOURCE_SHA=$(sha256sum "v${VERSION}.tar.gz" | cut -d' ' -f1) | |
| echo "Calculated SHA256: ${SOURCE_SHA}" | |
| # Create formula directory | |
| mkdir -p Formula | |
| # Create formula file | |
| printf 'class Mcpproxy < Formula\n desc "Smart MCP Proxy - Intelligent tool discovery and proxying for Model Context Protocol servers"\n homepage "https://github.com/smart-mcp-proxy/mcpproxy-go"\n url "https://github.com/smart-mcp-proxy/mcpproxy-go/archive/refs/tags/v%s.tar.gz"\n sha256 "%s"\n license "MIT"\n head "https://github.com/smart-mcp-proxy/mcpproxy-go.git"\n\n depends_on "go" => :build\n\n def install\n system "go", "build", "-ldflags", "-s -w -X mcpproxy-go/cmd/mcpproxy.version=v%s -X main.version=v%s", "-o", "mcpproxy", "./cmd/mcpproxy"\n bin.install "mcpproxy"\n end\n\n test do\n assert_match version.to_s, shell_output("#{bin}/mcpproxy --version")\n end\nend\n' "${VERSION}" "${SOURCE_SHA}" "${VERSION}" "${VERSION}" > Formula/mcpproxy.rb | |
| echo "Formula created successfully" | |
| cat Formula/mcpproxy.rb | |
| # Clean up | |
| rm -f *.tar.gz | |
| - name: Commit and push changes | |
| run: | | |
| cd tap | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git add Formula/mcpproxy.rb | |
| # Check if there are changes to commit | |
| if git diff --staged --quiet; then | |
| echo "No changes to commit" | |
| else | |
| git commit -m "Update mcpproxy to ${GITHUB_REF#refs/tags/v}" | |
| git push | |
| echo "Changes committed and pushed successfully" | |
| fi |