Skip to content

🐛 fix(ci): add GhosttyKit stub to release-ios workflow #4

🐛 fix(ci): add GhosttyKit stub to release-ios workflow

🐛 fix(ci): add GhosttyKit stub to release-ios workflow #4

Workflow file for this run

name: Release iOS
on:
push:
tags:
- "ios-v*"
workflow_dispatch:
inputs:
version:
description: "Version (e.g. 0.1.0)"
required: true
build_number:
description: "Build number"
required: true
default: "1"
permissions:
contents: write
concurrency:
group: release-ios-${{ github.ref }}
cancel-in-progress: false
jobs:
release-ios:
runs-on: macos-15
timeout-minutes: 30
env:
DERIVED_DATA: .derived-data-ios
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Resolve version
id: version
run: |
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
VERSION="${{ github.event.inputs.version }}"
BUILD_NUMBER="${{ github.event.inputs.build_number }}"
else
VERSION="${GITHUB_REF_NAME#ios-v}"
BUILD_NUMBER="${GITHUB_RUN_NUMBER}"
fi
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "build_number=${BUILD_NUMBER}" >> "$GITHUB_OUTPUT"
echo "📦 Version: ${VERSION} (${BUILD_NUMBER})"
- name: Install tools
run: |
brew install xcodegen xcbeautify
- name: Cache SPM packages
uses: actions/cache@v5
with:
path: ${{ env.DERIVED_DATA }}/SourcePackages
key: spm-ios-${{ hashFiles('MoriRemote/MoriRemote.xcodeproj/project.pbxproj', '**/Package.resolved') }}
restore-keys: spm-ios-
- name: Generate Xcode project
run: cd MoriRemote && xcodegen generate
- name: Stub GhosttyKit for SPM resolution
run: |
mkdir -p Frameworks/GhosttyKit.xcframework/macos-arm64_x86_64/GhosttyKit.framework/Headers
touch Frameworks/GhosttyKit.xcframework/macos-arm64_x86_64/GhosttyKit.framework/GhosttyKit
cat > Frameworks/GhosttyKit.xcframework/Info.plist << 'PLIST'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AvailableLibraries</key>
<array>
<dict>
<key>LibraryIdentifier</key>
<string>macos-arm64_x86_64</string>
<key>LibraryPath</key>
<string>GhosttyKit.framework</string>
<key>SupportedArchitectures</key>
<array><string>arm64</string><string>x86_64</string></array>
<key>SupportedPlatform</key>
<string>macos</string>
</dict>
</array>
<key>CFBundlePackageType</key>
<string>XFWK</string>
<key>XCFrameworkFormatVersion</key>
<string>1.0</string>
</dict>
</plist>
PLIST
- name: Import signing certificate
env:
APPLE_CERTIFICATE_P12: ${{ secrets.IOS_CERTIFICATE_P12 }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.IOS_CERTIFICATE_PASSWORD }}
run: |
KEYCHAIN_PATH="$RUNNER_TEMP/signing.keychain-db"
KEYCHAIN_PASSWORD="$(openssl rand -base64 32)"
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
CERT_PATH="$RUNNER_TEMP/certificate.p12"
echo "$APPLE_CERTIFICATE_P12" | base64 --decode > "$CERT_PATH"
security import "$CERT_PATH" \
-k "$KEYCHAIN_PATH" \
-P "$APPLE_CERTIFICATE_PASSWORD" \
-T /usr/bin/codesign \
-T /usr/bin/security
rm -f "$CERT_PATH"
security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security list-keychains -d user -s "$KEYCHAIN_PATH" login.keychain-db
- name: Archive
env:
VERSION: ${{ steps.version.outputs.version }}
BUILD_NUMBER: ${{ steps.version.outputs.build_number }}
DEVELOPMENT_TEAM: ${{ secrets.APPLE_TEAM_ID }}
run: |
set -euo pipefail
xcodebuild archive \
-project MoriRemote/MoriRemote.xcodeproj \
-scheme MoriRemote \
-destination 'generic/platform=iOS' \
-archivePath "$DERIVED_DATA/MoriRemote.xcarchive" \
-derivedDataPath "$DERIVED_DATA" \
-allowProvisioningUpdates \
MARKETING_VERSION="$VERSION" \
CURRENT_PROJECT_VERSION="$BUILD_NUMBER" \
2>&1 | xcbeautify
echo "✅ Archive created"
- name: Export IPA
run: |
set -euo pipefail
xcodebuild -exportArchive \
-archivePath "$DERIVED_DATA/MoriRemote.xcarchive" \
-exportOptionsPlist MoriRemote/ExportOptions.plist \
-exportPath "$DERIVED_DATA/export" \
-allowProvisioningUpdates \
2>&1 | xcbeautify
echo "✅ IPA exported"
- name: Upload to TestFlight
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }}
run: |
set -euo pipefail
IPA_PATH=$(find "$DERIVED_DATA/export" -name "*.ipa" | head -1)
if [ -z "$IPA_PATH" ]; then
echo "❌ No IPA found"
exit 1
fi
xcrun altool --upload-app \
--type ios \
--file "$IPA_PATH" \
--username "$APPLE_ID" \
--password "$APPLE_APP_PASSWORD"
echo "✅ Uploaded to TestFlight"
- name: Upload IPA artifact
uses: actions/upload-artifact@v5
with:
name: MoriRemote-${{ steps.version.outputs.version }}.ipa
path: ${{ env.DERIVED_DATA }}/export/*.ipa
retention-days: 90
- name: Cleanup
if: always()
run: |
KEYCHAIN_PATH="$RUNNER_TEMP/signing.keychain-db"
security delete-keychain "$KEYCHAIN_PATH" 2>/dev/null || true