Fix CI: patch broken SwiftTerm Package.swift before build #20
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: | |
| runs-on: macos-14 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Xcode | |
| uses: maxim-lobanov/setup-xcode@v1 | |
| with: | |
| xcode-version: latest-stable | |
| - name: Install create-dmg | |
| run: brew install create-dmg | |
| - name: Import signing certificate | |
| env: | |
| CERTIFICATE_BASE64: ${{ secrets.CERTIFICATE_BASE64 }} | |
| CERTIFICATE_PASSWORD: ${{ secrets.CERTIFICATE_PASSWORD }} | |
| KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} | |
| run: | | |
| # Create temporary keychain | |
| KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db | |
| security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH | |
| security set-keychain-settings -lut 21600 $KEYCHAIN_PATH | |
| security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH | |
| # Import certificate | |
| echo "$CERTIFICATE_BASE64" | base64 --decode > $RUNNER_TEMP/certificate.p12 | |
| security import $RUNNER_TEMP/certificate.p12 -P "$CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH | |
| # Set keychain as default and add to search list | |
| security default-keychain -s $KEYCHAIN_PATH | |
| security list-keychain -d user -s $KEYCHAIN_PATH login.keychain | |
| # Allow codesign to access the key without UI prompt | |
| security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH | |
| - name: Fix SwiftTerm manifest | |
| run: | | |
| # Resolve packages (will fail due to broken SwiftTerm manifest, but clones repos) | |
| xcodebuild -project app/AgentHub.xcodeproj \ | |
| -scheme AgentHub \ | |
| -resolvePackageDependencies || true | |
| # Fix trailing commas after commented-out swiftSettings in SwiftTerm Package.swift | |
| find ~/Library/Developer/Xcode/DerivedData -path "*/checkouts/SwiftTerm/Package.swift" \ | |
| -exec sed -i '' 's/exclude: platformExcludes + \["Mac\/README.md"\],/exclude: platformExcludes + ["Mac\/README.md"]/g' {} \; | |
| # Resolve again with the fixed manifest | |
| xcodebuild -project app/AgentHub.xcodeproj \ | |
| -scheme AgentHub \ | |
| -resolvePackageDependencies | |
| - name: Build app | |
| run: | | |
| xcodebuild -project app/AgentHub.xcodeproj \ | |
| -scheme AgentHub \ | |
| -configuration Release \ | |
| -archivePath build/AgentHub.xcarchive \ | |
| -disableAutomaticPackageResolution \ | |
| CODE_SIGN_STYLE=Manual \ | |
| CODE_SIGN_IDENTITY="Developer ID Application" \ | |
| DEVELOPMENT_TEAM=${{ secrets.TEAM_ID }} \ | |
| archive | |
| - name: Export app | |
| run: | | |
| xcodebuild -exportArchive \ | |
| -archivePath build/AgentHub.xcarchive \ | |
| -exportPath build \ | |
| -exportOptionsPlist app/ExportOptions.plist | |
| - name: Notarize app | |
| env: | |
| APPLE_ID: ${{ secrets.APPLE_ID }} | |
| TEAM_ID: ${{ secrets.TEAM_ID }} | |
| APP_PASSWORD: ${{ secrets.APP_PASSWORD }} | |
| run: | | |
| # Create zip for notarization | |
| ditto -c -k --keepParent build/AgentHub.app build/AgentHub.zip | |
| # Submit for notarization | |
| xcrun notarytool submit build/AgentHub.zip \ | |
| --apple-id "$APPLE_ID" \ | |
| --team-id "$TEAM_ID" \ | |
| --password "$APP_PASSWORD" \ | |
| --wait | |
| # Staple the ticket | |
| xcrun stapler staple build/AgentHub.app | |
| - name: Create zip for Sparkle updates | |
| run: | | |
| cd build | |
| # Re-create zip after stapling (the notarization zip was created before stapling) | |
| rm -f AgentHub.zip | |
| ditto -c -k --keepParent AgentHub.app AgentHub.app.zip | |
| # Get file size for appcast.xml | |
| stat -f%z AgentHub.app.zip > AgentHub.app.zip.size | |
| echo "Zip file size: $(cat AgentHub.app.zip.size) bytes" | |
| - name: Generate Sparkle signature | |
| env: | |
| SPARKLE_PRIVATE_KEY: ${{ secrets.SPARKLE_PRIVATE_KEY }} | |
| run: | | |
| if [ -z "$SPARKLE_PRIVATE_KEY" ]; then | |
| echo "SPARKLE_PRIVATE_KEY not set. Skipping Sparkle signature generation." | |
| exit 0 | |
| fi | |
| # Find the Sparkle sign_update tool | |
| SIGN_UPDATE=$(find ~/Library/Developer/Xcode/DerivedData -name sign_update -type f 2>/dev/null | head -1) | |
| if [ -n "$SIGN_UPDATE" ]; then | |
| echo "$SPARKLE_PRIVATE_KEY" | "$SIGN_UPDATE" build/AgentHub.app.zip --ed-key-file - > build/sparkle-signature.txt | |
| cat build/sparkle-signature.txt | |
| else | |
| echo "Warning: sign_update tool not found. Skipping Sparkle signature generation." | |
| echo "You can generate the signature manually after download." | |
| fi | |
| - name: Create DMG | |
| run: | | |
| create-dmg \ | |
| --volname "AgentHub" \ | |
| --window-pos 200 120 \ | |
| --window-size 600 400 \ | |
| --icon-size 100 \ | |
| --icon "AgentHub.app" 150 185 \ | |
| --app-drop-link 450 185 \ | |
| --hide-extension "AgentHub.app" \ | |
| build/AgentHub.dmg \ | |
| build/AgentHub.app | |
| - name: Notarize DMG | |
| env: | |
| APPLE_ID: ${{ secrets.APPLE_ID }} | |
| TEAM_ID: ${{ secrets.TEAM_ID }} | |
| APP_PASSWORD: ${{ secrets.APP_PASSWORD }} | |
| run: | | |
| # Submit DMG for notarization | |
| xcrun notarytool submit build/AgentHub.dmg \ | |
| --apple-id "$APPLE_ID" \ | |
| --team-id "$TEAM_ID" \ | |
| --password "$APP_PASSWORD" \ | |
| --wait | |
| # Staple the ticket to the DMG | |
| xcrun stapler staple build/AgentHub.dmg | |
| - name: Generate checksums | |
| run: | | |
| cd build | |
| shasum -a 256 AgentHub.dmg > AgentHub.dmg.sha256 | |
| shasum -a 256 AgentHub.app.zip > AgentHub.app.zip.sha256 | |
| cat AgentHub.dmg.sha256 | |
| cat AgentHub.app.zip.sha256 | |
| - name: Create Release | |
| uses: softprops/action-gh-release@v1 | |
| with: | |
| files: | | |
| build/AgentHub.dmg | |
| build/AgentHub.dmg.sha256 | |
| build/AgentHub.app.zip | |
| build/AgentHub.app.zip.sha256 | |
| build/sparkle-signature.txt | |
| draft: false | |
| prerelease: false | |
| generate_release_notes: true | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Update appcast.xml | |
| run: | | |
| VERSION="${GITHUB_REF_NAME#v}" | |
| PUB_DATE=$(date -u "+%a, %d %b %Y %H:%M:%S %z") | |
| # Parse Sparkle signature | |
| if [ ! -f build/sparkle-signature.txt ]; then | |
| echo "Warning: sparkle-signature.txt not found. Skipping appcast update." | |
| exit 0 | |
| fi | |
| ED_SIGNATURE=$(grep -o 'edSignature="[^"]*"' build/sparkle-signature.txt | head -1 | sed 's/edSignature="//;s/"//') | |
| if [ -z "$ED_SIGNATURE" ]; then | |
| ED_SIGNATURE=$(cat build/sparkle-signature.txt | head -1) | |
| fi | |
| LENGTH=$(cat build/AgentHub.app.zip.size) | |
| echo "Version: $VERSION" | |
| echo "Signature: $ED_SIGNATURE" | |
| echo "Length: $LENGTH" | |
| # Build the new <item> block and write to file | |
| { | |
| echo ' <item>' | |
| echo " <title>Version ${VERSION}</title>" | |
| echo " <link>https://github.com/jamesrochabrun/AgentHub/releases/tag/v${VERSION}</link>" | |
| echo " <sparkle:version>${VERSION}</sparkle:version>" | |
| echo " <sparkle:shortVersionString>${VERSION}</sparkle:shortVersionString>" | |
| echo ' <description><![CDATA[' | |
| echo " <h2>Version ${VERSION}</h2>" | |
| echo ' <ul>' | |
| echo ' <li>See release notes on GitHub</li>' | |
| echo ' </ul>' | |
| echo ' ]]></description>' | |
| echo " <pubDate>${PUB_DATE}</pubDate>" | |
| echo ' <enclosure' | |
| echo " url=\"https://github.com/jamesrochabrun/AgentHub/releases/download/v${VERSION}/AgentHub.app.zip\"" | |
| echo " sparkle:version=\"${VERSION}\"" | |
| echo " sparkle:shortVersionString=\"${VERSION}\"" | |
| echo " sparkle:edSignature=\"${ED_SIGNATURE}\"" | |
| echo " length=\"${LENGTH}\"" | |
| echo ' type="application/octet-stream" />' | |
| echo ' <sparkle:minimumSystemVersion>14.0</sparkle:minimumSystemVersion>' | |
| echo ' </item>' | |
| } > build/new_item.xml | |
| # Switch to main branch and update appcast.xml | |
| git fetch origin main | |
| git checkout main | |
| # Insert new item as first <item> in appcast.xml (after the <language> line) | |
| awk '/<language>en<\/language>/{print; print ""; while ((getline line < "build/new_item.xml") > 0) print line; next}1' appcast.xml > appcast_tmp.xml | |
| mv appcast_tmp.xml appcast.xml | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git add appcast.xml | |
| git commit -m "Update appcast.xml for v${VERSION}" | |
| git push origin main |