Tip Release #5
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: Tip Release | |
| on: | |
| workflow_dispatch: # manual trigger from any branch via Actions UI | |
| permissions: | |
| contents: write | |
| jobs: | |
| tip: | |
| runs-on: macos-26 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install signing certificate | |
| env: | |
| CERTIFICATE_P12: ${{ secrets.CERTIFICATE_P12 }} | |
| CERTIFICATE_PASSWORD: ${{ secrets.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 "$CERTIFICATE_P12" | base64 --decode > "$CERT_PATH" | |
| security import "$CERT_PATH" \ | |
| -P "$CERTIFICATE_PASSWORD" \ | |
| -A \ | |
| -t cert \ | |
| -f pkcs12 \ | |
| -k "$KEYCHAIN_PATH" | |
| rm -f "$CERT_PATH" | |
| security list-keychains -d user -s "$KEYCHAIN_PATH" $(security list-keychains -d user | tr -d '"') | |
| security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" | |
| IDENTITY=$(security find-identity -v -p codesigning "$KEYCHAIN_PATH" | head -1 | sed 's/.*"\(.*\)".*/\1/') | |
| echo "SIGNING_IDENTITY=$IDENTITY" >> "$GITHUB_ENV" | |
| - name: Set up notarization credentials | |
| env: | |
| APPLE_ID: ${{ secrets.APPLE_ID }} | |
| APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
| APP_SPECIFIC_PASSWORD: ${{ secrets.APP_SPECIFIC_PASSWORD }} | |
| run: | | |
| xcrun notarytool store-credentials "FreeWispr-notarize" \ | |
| --apple-id "$APPLE_ID" \ | |
| --team-id "$APPLE_TEAM_ID" \ | |
| --password "$APP_SPECIFIC_PASSWORD" | |
| - name: Build release binary | |
| run: | | |
| cd FreeWispr | |
| swift build -c release --arch arm64 | |
| - name: Assemble .app bundle | |
| run: | | |
| APP_NAME="FreeWispr" | |
| SHORT_SHA="$(git rev-parse --short HEAD)" | |
| BUILD_DIR="build" | |
| APP_BUNDLE="$BUILD_DIR/$APP_NAME.app" | |
| mkdir -p "$APP_BUNDLE/Contents/MacOS" | |
| mkdir -p "$APP_BUNDLE/Contents/Resources" | |
| cp "FreeWispr/.build/arm64-apple-macosx/release/$APP_NAME" "$APP_BUNDLE/Contents/MacOS/$APP_NAME" | |
| cp "FreeWispr/Sources/$APP_NAME/Info.plist" "$APP_BUNDLE/Contents/Info.plist" | |
| cp "FreeWispr/Sources/$APP_NAME/Resources/AppIcon.icns" "$APP_BUNDLE/Contents/Resources/" | |
| cp -R "FreeWispr/.build/arm64-apple-macosx/release/${APP_NAME}_${APP_NAME}Core.bundle" "$APP_BUNDLE/Contents/Resources/" | |
| # Stamp version as <plist-version>-tip+<sha> | |
| PLIST_VERSION=$(/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" "$APP_BUNDLE/Contents/Info.plist") | |
| TIP_VERSION="${PLIST_VERSION}-tip+${SHORT_SHA}" | |
| /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $TIP_VERSION" "$APP_BUNDLE/Contents/Info.plist" | |
| /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $TIP_VERSION" "$APP_BUNDLE/Contents/Info.plist" | |
| - name: Code sign | |
| run: | | |
| codesign --force --deep \ | |
| --sign "$SIGNING_IDENTITY" \ | |
| --options runtime \ | |
| --entitlements "FreeWispr/FreeWispr.entitlements" \ | |
| --timestamp \ | |
| "build/FreeWispr.app" | |
| - name: Notarize | |
| run: | | |
| ditto -c -k --keepParent "build/FreeWispr.app" "build/FreeWispr-notarize.zip" | |
| xcrun notarytool submit "build/FreeWispr-notarize.zip" \ | |
| --keychain-profile "FreeWispr-notarize" \ | |
| --wait | |
| rm -f "build/FreeWispr-notarize.zip" | |
| xcrun stapler staple "build/FreeWispr.app" | |
| - name: Create DMG | |
| run: | | |
| DMG_PATH="build/FreeWispr-tip.dmg" | |
| DMG_STAGING="build/dmg-staging" | |
| mkdir -p "$DMG_STAGING" | |
| cp -R "build/FreeWispr.app" "$DMG_STAGING/" | |
| ln -s /Applications "$DMG_STAGING/Applications" | |
| hdiutil create \ | |
| -volname "FreeWispr" \ | |
| -srcfolder "$DMG_STAGING" \ | |
| -ov \ | |
| -format UDZO \ | |
| "$DMG_PATH" | |
| rm -rf "$DMG_STAGING" | |
| codesign --force --sign "$SIGNING_IDENTITY" --timestamp "$DMG_PATH" | |
| - name: Update tip release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| SHORT_SHA="$(git rev-parse --short HEAD)" | |
| BRANCH="${GITHUB_REF_NAME}" | |
| DATE="$(date -u +%Y-%m-%d)" | |
| TAG="tip-${SHORT_SHA}" | |
| # Delete previous tip-* releases (keep the tag to avoid ruleset conflicts) | |
| gh release list --json tagName,isPrerelease -q '.[] | select(.isPrerelease and (.tagName | startswith("tip-"))) | .tagName' \ | |
| | while read -r old_tag; do | |
| gh release delete "$old_tag" --yes 2>/dev/null || true | |
| git push origin :"refs/tags/$old_tag" 2>/dev/null || true | |
| done | |
| git tag "$TAG" | |
| git push origin "$TAG" | |
| gh release create "$TAG" \ | |
| build/FreeWispr-tip.dmg \ | |
| --title "FreeWispr tip (${SHORT_SHA})" \ | |
| --notes "$(cat <<EOF | |
| Built from \`${SHORT_SHA}\` on branch \`${BRANCH}\` (${DATE}). | |
| **This is a pre-release build for testing.** | |
| For daily use, grab the [latest stable release](https://github.com/ygivenx/freeWispr/releases/latest). | |
| EOF | |
| )" \ | |
| --prerelease | |
| - name: Clean up keychain | |
| if: always() | |
| run: | | |
| KEYCHAIN_PATH="$RUNNER_TEMP/signing.keychain-db" | |
| security delete-keychain "$KEYCHAIN_PATH" 2>/dev/null || true |