Release macOS App #1
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 macOS App | |
| # Disabled until GitHub secrets are configured for code signing | |
| # To enable: add APPLE_CERTIFICATE_P12, APPLE_CERTIFICATE_PASSWORD, | |
| # APPLE_ID, APPLE_ID_PASSWORD, APPLE_TEAM_ID secrets | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: 'Release tag to attach artifact to (e.g., v0.1.0)' | |
| required: true | |
| # Uncomment to auto-trigger on release: | |
| # release: | |
| # types: [published] | |
| workflow_call: | |
| inputs: | |
| tag: | |
| description: 'Release tag to attach artifact to' | |
| required: true | |
| type: string | |
| env: | |
| APP_NAME: "Hack Desktop" | |
| SCHEME: HackDesktop | |
| PROJECT_PATH: apps/macos | |
| jobs: | |
| build-and-notarize: | |
| runs-on: macos-14 | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Xcode | |
| uses: maxim-lobanov/setup-xcode@v1 | |
| with: | |
| xcode-version: '15.4' | |
| - name: Install xcodegen | |
| run: brew install xcodegen | |
| - name: Generate Xcode project | |
| working-directory: ${{ env.PROJECT_PATH }} | |
| run: xcodegen generate | |
| - name: Import certificate | |
| env: | |
| CERTIFICATE_P12: ${{ secrets.APPLE_CERTIFICATE_P12 }} | |
| CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} | |
| run: | | |
| # Create temporary keychain | |
| KEYCHAIN_PATH=$RUNNER_TEMP/app-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" | |
| # Import certificate | |
| echo "$CERTIFICATE_P12" | base64 --decode > $RUNNER_TEMP/certificate.p12 | |
| security import $RUNNER_TEMP/certificate.p12 \ | |
| -P "$CERTIFICATE_PASSWORD" \ | |
| -A \ | |
| -t cert \ | |
| -f pkcs12 \ | |
| -k "$KEYCHAIN_PATH" | |
| # Allow codesign to access keychain | |
| security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" | |
| # Add to search list | |
| security list-keychain -d user -s "$KEYCHAIN_PATH" | |
| echo "KEYCHAIN_PATH=$KEYCHAIN_PATH" >> $GITHUB_ENV | |
| - name: Build and archive | |
| working-directory: ${{ env.PROJECT_PATH }} | |
| env: | |
| KEYCHAIN: ${{ env.KEYCHAIN_PATH }} | |
| run: | | |
| xcodebuild archive \ | |
| -scheme "$SCHEME" \ | |
| -configuration Release \ | |
| -archivePath "$RUNNER_TEMP/HackDesktop.xcarchive" \ | |
| CODE_SIGN_STYLE=Manual \ | |
| CODE_SIGN_IDENTITY="Developer ID Application" \ | |
| OTHER_CODE_SIGN_FLAGS="--keychain $KEYCHAIN_PATH" | |
| - name: Export app | |
| env: | |
| TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
| run: | | |
| cat > $RUNNER_TEMP/ExportOptions.plist << EOF | |
| <?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>method</key> | |
| <string>developer-id</string> | |
| <key>teamID</key> | |
| <string>$TEAM_ID</string> | |
| <key>signingStyle</key> | |
| <string>manual</string> | |
| <key>signingCertificate</key> | |
| <string>Developer ID Application</string> | |
| </dict> | |
| </plist> | |
| EOF | |
| xcodebuild -exportArchive \ | |
| -archivePath "$RUNNER_TEMP/HackDesktop.xcarchive" \ | |
| -exportPath "$RUNNER_TEMP/export" \ | |
| -exportOptionsPlist "$RUNNER_TEMP/ExportOptions.plist" | |
| - name: Notarize app | |
| env: | |
| APPLE_ID: ${{ secrets.APPLE_ID }} | |
| APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} | |
| APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
| run: | | |
| # Create ZIP for notarization | |
| ditto -c -k --keepParent "$RUNNER_TEMP/export/$APP_NAME.app" "$RUNNER_TEMP/app.zip" | |
| # Submit for notarization | |
| xcrun notarytool submit "$RUNNER_TEMP/app.zip" \ | |
| --apple-id "$APPLE_ID" \ | |
| --password "$APPLE_ID_PASSWORD" \ | |
| --team-id "$APPLE_TEAM_ID" \ | |
| --wait | |
| # Staple the notarization ticket | |
| xcrun stapler staple "$RUNNER_TEMP/export/$APP_NAME.app" | |
| - name: Create DMG | |
| run: | | |
| # Install create-dmg | |
| brew install create-dmg | |
| # Create DMG | |
| create-dmg \ | |
| --volname "$APP_NAME" \ | |
| --window-pos 200 120 \ | |
| --window-size 600 400 \ | |
| --icon-size 100 \ | |
| --icon "$APP_NAME.app" 150 190 \ | |
| --app-drop-link 450 190 \ | |
| --hide-extension "$APP_NAME.app" \ | |
| "$RUNNER_TEMP/HackDesktop.dmg" \ | |
| "$RUNNER_TEMP/export/$APP_NAME.app" || true | |
| # Fallback if create-dmg fails (it returns non-zero even on success sometimes) | |
| if [ ! -f "$RUNNER_TEMP/HackDesktop.dmg" ]; then | |
| hdiutil create -volname "$APP_NAME" \ | |
| -srcfolder "$RUNNER_TEMP/export/$APP_NAME.app" \ | |
| -ov -format UDZO \ | |
| "$RUNNER_TEMP/HackDesktop.dmg" | |
| fi | |
| - name: Notarize DMG | |
| env: | |
| APPLE_ID: ${{ secrets.APPLE_ID }} | |
| APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} | |
| APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
| run: | | |
| xcrun notarytool submit "$RUNNER_TEMP/HackDesktop.dmg" \ | |
| --apple-id "$APPLE_ID" \ | |
| --password "$APPLE_ID_PASSWORD" \ | |
| --team-id "$APPLE_TEAM_ID" \ | |
| --wait | |
| xcrun stapler staple "$RUNNER_TEMP/HackDesktop.dmg" | |
| - name: Get version | |
| id: version | |
| run: | | |
| VERSION=$(defaults read "$RUNNER_TEMP/export/$APP_NAME.app/Contents/Info.plist" CFBundleShortVersionString) | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| - name: Rename artifacts | |
| env: | |
| VERSION: ${{ steps.version.outputs.version }} | |
| run: | | |
| mv "$RUNNER_TEMP/HackDesktop.dmg" "$RUNNER_TEMP/HackDesktop-$VERSION-macOS.dmg" | |
| # Also create a ZIP of the app | |
| ditto -c -k --keepParent \ | |
| "$RUNNER_TEMP/export/$APP_NAME.app" \ | |
| "$RUNNER_TEMP/HackDesktop-$VERSION-macOS.zip" | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: macos-app | |
| path: | | |
| ${{ runner.temp }}/HackDesktop-*.dmg | |
| ${{ runner.temp }}/HackDesktop-*.zip | |
| - name: Upload to release | |
| if: github.event_name == 'release' || github.event.inputs.tag != '' || inputs.tag != '' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| RELEASE_TAG: ${{ github.event.release.tag_name || github.event.inputs.tag || inputs.tag }} | |
| VERSION: ${{ steps.version.outputs.version }} | |
| run: | | |
| gh release upload "$RELEASE_TAG" \ | |
| "$RUNNER_TEMP/HackDesktop-$VERSION-macOS.dmg" \ | |
| "$RUNNER_TEMP/HackDesktop-$VERSION-macOS.zip" \ | |
| --clobber | |
| - name: Cleanup keychain | |
| if: always() | |
| run: | | |
| if [ -n "$KEYCHAIN_PATH" ] && [ -f "$KEYCHAIN_PATH" ]; then | |
| security delete-keychain "$KEYCHAIN_PATH" || true | |
| fi |