Skip to content

Fix CI: patch broken SwiftTerm Package.swift before build #20

Fix CI: patch broken SwiftTerm Package.swift before build

Fix CI: patch broken SwiftTerm Package.swift before build #20

Workflow file for this run

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