Release VoiceScribe #4
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 VoiceScribe | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: 'Version number (e.g., 1.0.0)' | |
| required: true | |
| type: string | |
| jobs: | |
| build-and-release: | |
| runs-on: macos-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v5 | |
| - name: Set up Xcode | |
| uses: maxim-lobanov/setup-xcode@v1 | |
| with: | |
| xcode-version: latest-stable | |
| - name: Update version in project | |
| run: | | |
| # Update MARKETING_VERSION in project.pbxproj | |
| sed -i '' "s/MARKETING_VERSION = [^;]*/MARKETING_VERSION = ${{ inputs.version }}/g" VoiceScribe.xcodeproj/project.pbxproj | |
| # Extract major version number for CURRENT_PROJECT_VERSION (e.g., 1.0.0 -> 1) | |
| BUILD_NUMBER=$(echo "${{ inputs.version }}" | sed 's/[^0-9]*\([0-9]*\).*/\1/') | |
| sed -i '' "s/CURRENT_PROJECT_VERSION = [^;]*/CURRENT_PROJECT_VERSION = $BUILD_NUMBER/g" VoiceScribe.xcodeproj/project.pbxproj | |
| echo "Updated version to ${{ inputs.version }} (build $BUILD_NUMBER)" | |
| - name: Install Apple certificate | |
| env: | |
| APPLE_CERTIFICATE_BASE64: ${{ secrets.APPLE_CERTIFICATE_BASE64 }} | |
| APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} | |
| run: | | |
| KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db | |
| KEYCHAIN_PASSWORD=$(openssl rand -base64 32) | |
| echo "$APPLE_CERTIFICATE_BASE64" | base64 --decode > $RUNNER_TEMP/certificate.p12 | |
| security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH | |
| security set-keychain-settings -lut 21600 $KEYCHAIN_PATH | |
| security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH | |
| security import $RUNNER_TEMP/certificate.p12 \ | |
| -P "$APPLE_CERTIFICATE_PASSWORD" \ | |
| -A -t cert -f pkcs12 \ | |
| -k $KEYCHAIN_PATH | |
| security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH | |
| security list-keychain -d user -s $KEYCHAIN_PATH | |
| rm -f $RUNNER_TEMP/certificate.p12 | |
| echo "KEYCHAIN_PATH=$KEYCHAIN_PATH" >> $GITHUB_ENV | |
| - name: Build app | |
| env: | |
| APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
| run: | | |
| xcodebuild clean build \ | |
| -project VoiceScribe.xcodeproj \ | |
| -scheme VoiceScribe \ | |
| -configuration Release \ | |
| -derivedDataPath ./build \ | |
| -arch x86_64 -arch arm64 \ | |
| CODE_SIGN_STYLE=Manual \ | |
| CODE_SIGN_IDENTITY="Developer ID Application" \ | |
| DEVELOPMENT_TEAM="$APPLE_TEAM_ID" \ | |
| CODE_SIGN_INJECT_BASE_ENTITLEMENTS=NO \ | |
| OTHER_CODE_SIGN_FLAGS="--timestamp --options runtime" | |
| - name: Verify code signature | |
| run: | | |
| APP_PATH="build/Build/Products/Release/VoiceScribe.app" | |
| echo "Verifying code signature..." | |
| codesign -vvv --deep --strict "$APP_PATH" | |
| echo "Checking signature details..." | |
| codesign -dvv "$APP_PATH" | |
| echo "Checking entitlements..." | |
| codesign -d --entitlements - "$APP_PATH" | |
| - name: Notarize app | |
| env: | |
| APP_STORE_CONNECT_API_KEY_BASE64: ${{ secrets.APP_STORE_CONNECT_API_KEY_BASE64 }} | |
| APP_STORE_CONNECT_KEY_ID: ${{ secrets.APP_STORE_CONNECT_KEY_ID }} | |
| APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }} | |
| run: | | |
| APP_PATH="build/Build/Products/Release/VoiceScribe.app" | |
| echo "$APP_STORE_CONNECT_API_KEY_BASE64" | base64 --decode > $RUNNER_TEMP/AuthKey.p8 | |
| /usr/bin/ditto -c -k --keepParent "$APP_PATH" "$RUNNER_TEMP/VoiceScribe-notarize.zip" | |
| echo "Submitting for notarization..." | |
| SUBMIT_OUTPUT=$(xcrun notarytool submit "$RUNNER_TEMP/VoiceScribe-notarize.zip" \ | |
| --key "$RUNNER_TEMP/AuthKey.p8" \ | |
| --key-id "$APP_STORE_CONNECT_KEY_ID" \ | |
| --issuer "$APP_STORE_CONNECT_ISSUER_ID" \ | |
| --wait 2>&1) | |
| echo "$SUBMIT_OUTPUT" | |
| # Extract submission ID | |
| SUBMISSION_ID=$(echo "$SUBMIT_OUTPUT" | grep "id:" | head -1 | awk '{print $2}') | |
| # Check if notarization succeeded | |
| if echo "$SUBMIT_OUTPUT" | grep -q "status: Accepted"; then | |
| echo "Notarization succeeded!" | |
| else | |
| echo "Notarization failed. Fetching detailed log..." | |
| xcrun notarytool log "$SUBMISSION_ID" \ | |
| --key "$RUNNER_TEMP/AuthKey.p8" \ | |
| --key-id "$APP_STORE_CONNECT_KEY_ID" \ | |
| --issuer "$APP_STORE_CONNECT_ISSUER_ID" | |
| rm -f $RUNNER_TEMP/AuthKey.p8 $RUNNER_TEMP/VoiceScribe-notarize.zip | |
| exit 1 | |
| fi | |
| echo "Stapling notarization ticket..." | |
| xcrun stapler staple "$APP_PATH" | |
| echo "Verifying..." | |
| spctl -a -t exec -vv "$APP_PATH" | |
| xcrun stapler validate "$APP_PATH" | |
| rm -f $RUNNER_TEMP/AuthKey.p8 $RUNNER_TEMP/VoiceScribe-notarize.zip | |
| - name: Create ZIP | |
| run: | | |
| cd build/Build/Products/Release | |
| xattr -cr VoiceScribe.app | |
| find VoiceScribe.app -name '._*' -delete | |
| zip -r ../../../../VoiceScribe-${{ inputs.version }}.zip VoiceScribe.app | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: v${{ inputs.version }} | |
| name: VoiceScribe v${{ inputs.version }} | |
| body: | | |
| ## VoiceScribe v${{ inputs.version }} | |
| ### Installation | |
| 1. Download `VoiceScribe-${{ inputs.version }}.zip` | |
| 2. Unzip and move `VoiceScribe.app` to Applications | |
| 3. Double-click to open | |
| This release is **signed and notarized** by Apple. | |
| ### Features | |
| - **Global hotkey recording** - Press Option-Shift-Space to record from anywhere (customizable) | |
| - **Dual transcription engines** - Choose between privacy-focused local [WhisperKit](https://github.com/argmaxinc/WhisperKit) (on-device, Apple Silicon only) or cloud-based OpenAI Transcription | |
| - **Multiple AI models** - Download and switch between WhisperKit models (Base/Small/Medium) or select OpenAI models (Whisper V2/GPT-4o/GPT-4o Mini) | |
| - **AI-powered enhancement** - Optional post-processing to add perfect punctuation, capitalization, and formatting | |
| - **Smart paste** - Transcriptions paste directly into your active app | |
| - **Transcription history** - Review past transcriptions anytime | |
| - **Secure storage** - API keys encrypted in macOS Keychain | |
| ### Requirements | |
| - macOS 14.0 (Sonoma) or later | |
| - Apple Silicon (M-series) Mac for local WhisperKit transcription and MLX enhancement | |
| - Intel Macs supported with OpenAI Transcription API | |
| ### Privacy | |
| - All audio processing happens on-device with [WhisperKit](https://github.com/argmaxinc/WhisperKit) (no network transmission) | |
| - OpenAI mode sends audio to their servers - review [OpenAI's Privacy Policy](https://openai.com/policies/privacy-policy) | |
| - API keys encrypted in macOS Keychain with device-only access | |
| - No telemetry or analytics | |
| files: VoiceScribe-${{ inputs.version }}.zip | |
| draft: false | |
| prerelease: false | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Cleanup keychain | |
| if: always() | |
| run: | | |
| if [[ -f "${KEYCHAIN_PATH:-}" ]]; then | |
| security delete-keychain $KEYCHAIN_PATH || true | |
| fi |