feat(context): implement app-aware dictation with specialized prompting #30
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*' | |
| env: | |
| WHISPER_VERSION: "1.8.2" | |
| jobs: | |
| build-and-release: | |
| runs-on: macos-26 | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Xcode | |
| uses: maxim-lobanov/setup-xcode@v1 | |
| with: | |
| xcode-version: latest-stable | |
| - name: Install build tools | |
| run: brew install xcodegen create-dmg | |
| - name: Get version from tag | |
| id: version | |
| run: | | |
| VERSION=${GITHUB_REF#refs/tags/v} | |
| echo "VERSION=$VERSION" >> $GITHUB_OUTPUT | |
| echo "Building version: $VERSION" | |
| - name: Download whisper.cpp XCFramework | |
| run: | | |
| mkdir -p deps/whisper.cpp | |
| curl -L -o whisper-xcframework.zip \ | |
| "https://github.com/ggml-org/whisper.cpp/releases/download/v$WHISPER_VERSION/whisper-v$WHISPER_VERSION-xcframework.zip" | |
| unzip -q whisper-xcframework.zip -d deps/whisper.cpp | |
| rm whisper-xcframework.zip | |
| ls -la deps/whisper.cpp/build-apple/ | |
| echo "Downloaded whisper.cpp v$WHISPER_VERSION xcframework" | |
| - name: Download models | |
| run: make models | |
| - name: Generate Xcode project | |
| run: xcodegen generate | |
| - name: Build with xcodebuild | |
| run: | | |
| xcodebuild -project OpenDictation.xcodeproj \ | |
| -scheme OpenDictation \ | |
| -configuration Release \ | |
| -derivedDataPath "$RUNNER_TEMP/DerivedData" \ | |
| build | |
| echo "Build complete" | |
| ls -la "$RUNNER_TEMP/DerivedData/Build/Products/Release/" | |
| - name: Inject version into built app | |
| run: | | |
| VERSION="${{ steps.version.outputs.VERSION }}" | |
| APP_PATH="$RUNNER_TEMP/DerivedData/Build/Products/Release/OpenDictation.app" | |
| INFO_PLIST="$APP_PATH/Contents/Info.plist" | |
| echo "Injecting version into built app..." | |
| echo "Marketing version: $VERSION" | |
| echo "Build number: $GITHUB_RUN_NUMBER" | |
| # Set marketing version | |
| /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $VERSION" "$INFO_PLIST" | |
| # Set build number | |
| /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $GITHUB_RUN_NUMBER" "$INFO_PLIST" | |
| # Verify injection | |
| echo "Verification:" | |
| echo " CFBundleShortVersionString: $(/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" "$INFO_PLIST")" | |
| echo " CFBundleVersion: $(/usr/libexec/PlistBuddy -c "Print :CFBundleVersion" "$INFO_PLIST")" | |
| echo "Version injection complete" | |
| - name: Sign app bundle | |
| run: | | |
| APP_PATH="$RUNNER_TEMP/DerivedData/Build/Products/Release/OpenDictation.app" | |
| # Sign the app (ad-hoc signing) | |
| echo "Signing app bundle..." | |
| codesign --deep --force -s - "$APP_PATH" | |
| # Verify signature | |
| echo "Verifying signature..." | |
| codesign --verify --deep --strict "$APP_PATH" | |
| echo "App signed successfully" | |
| - name: Create DMG | |
| run: | | |
| VERSION="${{ steps.version.outputs.VERSION }}" | |
| DMG_NAME="OpenDictation.dmg" | |
| APP_PATH="$RUNNER_TEMP/DerivedData/Build/Products/Release/OpenDictation.app" | |
| create-dmg \ | |
| --volname "Open Dictation" \ | |
| --volicon "OpenDictation/Resources/DMG/VolumeIcon.icns" \ | |
| --background "OpenDictation/Resources/DMG/background.tiff" \ | |
| --window-pos 200 120 \ | |
| --window-size 500 400 \ | |
| --icon-size 70 \ | |
| --icon "OpenDictation.app" 100 200 \ | |
| --hide-extension "OpenDictation.app" \ | |
| --app-drop-link 350 200 \ | |
| "$RUNNER_TEMP/$DMG_NAME" \ | |
| "$APP_PATH" | |
| echo "DMG_PATH=$RUNNER_TEMP/$DMG_NAME" >> $GITHUB_ENV | |
| echo "DMG_NAME=$DMG_NAME" >> $GITHUB_ENV | |
| ls -lh "$RUNNER_TEMP/$DMG_NAME" | |
| - name: Sign update with Sparkle | |
| env: | |
| SPARKLE_KEY: ${{ secrets.SPARKLE_PRIVATE_KEY }} | |
| run: | | |
| if [ -z "$SPARKLE_KEY" ]; then | |
| echo "SPARKLE_SIGNATURE=" >> $GITHUB_ENV | |
| echo "Skipping Sparkle signing (no key configured)" | |
| exit 0 | |
| fi | |
| # Use deterministic path for Sparkle tools (following CodeEdit/TimeMachineStatus pattern) | |
| SPARKLE_BIN="$RUNNER_TEMP/DerivedData/SourcePackages/artifacts/sparkle/Sparkle/bin" | |
| if [ ! -f "$SPARKLE_BIN/sign_update" ]; then | |
| echo "Error: Sparkle sign_update not found at $SPARKLE_BIN" | |
| echo "SPARKLE_SIGNATURE=" >> $GITHUB_ENV | |
| exit 1 | |
| fi | |
| echo "Using Sparkle tools from: $SPARKLE_BIN" | |
| echo -n "$SPARKLE_KEY" > "$RUNNER_TEMP/sparkle_key" | |
| SIGNATURE=$("$SPARKLE_BIN/sign_update" --ed-key-file "$RUNNER_TEMP/sparkle_key" "$DMG_PATH" | grep "sparkle:edSignature" | cut -d'"' -f2) | |
| echo "SPARKLE_SIGNATURE=$SIGNATURE" >> $GITHUB_ENV | |
| rm "$RUNNER_TEMP/sparkle_key" | |
| - name: Get DMG size | |
| run: | | |
| DMG_SIZE=$(stat -f%z "$DMG_PATH") | |
| echo "DMG_SIZE=$DMG_SIZE" >> $GITHUB_ENV | |
| - name: Update appcast.xml | |
| run: | | |
| if [ -z "$SPARKLE_SIGNATURE" ]; then | |
| echo "Skipping appcast update (no signature)" | |
| exit 0 | |
| fi | |
| export VERSION="${{ steps.version.outputs.VERSION }}" | |
| export BUILD_NUMBER="${GITHUB_RUN_NUMBER}" | |
| export PUB_DATE=$(date -R) | |
| python3 << 'PYTHON_SCRIPT' | |
| import xml.etree.ElementTree as ET | |
| import os | |
| version = os.environ['VERSION'] | |
| build = os.environ['BUILD_NUMBER'] | |
| pub_date = os.environ['PUB_DATE'] | |
| dmg_name = os.environ['DMG_NAME'] | |
| signature = os.environ['SPARKLE_SIGNATURE'] | |
| size = os.environ['DMG_SIZE'] | |
| # Register Sparkle namespace | |
| ET.register_namespace('sparkle', 'http://www.andymatuschak.org/xml-namespaces/sparkle') | |
| ET.register_namespace('dc', 'http://purl.org/dc/elements/1.1/') | |
| tree = ET.parse('appcast.xml') | |
| root = tree.getroot() | |
| channel = root.find('channel') | |
| # Create new item | |
| item = ET.SubElement(channel, 'item') | |
| ET.SubElement(item, 'title').text = f'Version {version}' | |
| ET.SubElement(item, 'pubDate').text = pub_date | |
| ET.SubElement(item, '{http://www.andymatuschak.org/xml-namespaces/sparkle}version').text = build | |
| ET.SubElement(item, '{http://www.andymatuschak.org/xml-namespaces/sparkle}shortVersionString').text = version | |
| ET.SubElement(item, '{http://www.andymatuschak.org/xml-namespaces/sparkle}minimumSystemVersion').text = '14.0' | |
| enclosure = ET.SubElement(item, 'enclosure') | |
| enclosure.set('url', f'https://github.com/kdcokenny/OpenDictation/releases/download/v{version}/{dmg_name}') | |
| enclosure.set('{http://www.andymatuschak.org/xml-namespaces/sparkle}edSignature', signature) | |
| enclosure.set('length', size) | |
| enclosure.set('type', 'application/octet-stream') | |
| tree.write('appcast.xml', encoding='utf-8', xml_declaration=True) | |
| print(f'Updated appcast.xml with version {version}') | |
| PYTHON_SCRIPT | |
| - name: Commit appcast.xml | |
| run: | | |
| if [ -z "$SPARKLE_SIGNATURE" ]; then | |
| echo "Skipping appcast commit (no signature)" | |
| exit 0 | |
| fi | |
| 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 for v${{ steps.version.outputs.VERSION }}" | |
| git push origin HEAD:main | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| files: ${{ env.DMG_PATH }} | |
| generate_release_notes: true | |
| draft: false | |
| prerelease: ${{ contains(steps.version.outputs.VERSION, 'alpha') || contains(steps.version.outputs.VERSION, 'beta') }} |