fix(clips): restore ad-hoc meeting detection toast, gate on live mic … #274
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: Clips Desktop Release | |
| # Release channel for the Clips Tauri menu-bar app, SEPARATE from the main | |
| # Electron desktop app. Uses tag prefix `clips-v*` so the two release trains | |
| # don't collide on GitHub Releases. The in-app updater fetches its manifest | |
| # from the `clips-latest` stable-pointer release (assets overwritten each run), | |
| # which is what `endpoints` in tauri.conf.json points at. | |
| on: | |
| push: | |
| branches: [main] | |
| paths: | |
| - "templates/clips/desktop/**" | |
| - "packages/shared-app-config/**" | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: "Version (e.g. 0.2.0). Leave empty to use package.json version." | |
| required: false | |
| type: string | |
| permissions: | |
| contents: write | |
| env: | |
| TAURI_APP_PATH: templates/clips/desktop | |
| jobs: | |
| build-tauri: | |
| strategy: | |
| fail-fast: false | |
| # Serialize so platform jobs can't race to create the draft release at | |
| # the same time — when both call tauri-action concurrently, one job's | |
| # bundles + latest.json get orphaned (clips-v0.1.74 lost its macOS | |
| # assets this way, leaving the manifest Windows-only and breaking the | |
| # Tauri updater on macOS clients). | |
| max-parallel: 1 | |
| matrix: | |
| include: | |
| - os: macos-latest | |
| rust-targets: "aarch64-apple-darwin,x86_64-apple-darwin" | |
| args: "--target universal-apple-darwin" | |
| - os: windows-latest | |
| rust-targets: "" | |
| args: "" | |
| - os: ubuntu-22.04 | |
| rust-targets: "" | |
| args: "--bundles appimage,deb,rpm" | |
| runs-on: ${{ matrix.os }} | |
| env: | |
| MACOSX_DEPLOYMENT_TARGET: ${{ matrix.os == 'macos-latest' && '13.0' || '' }} | |
| GGML_METAL_MACOSX_VERSION_MIN: ${{ matrix.os == 'macos-latest' && '13.0' || '' }} | |
| outputs: | |
| app-version: ${{ steps.resolve-version.outputs.version }} | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| - uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 | |
| with: | |
| node-version: 22 | |
| cache: pnpm | |
| - name: Install Linux dependencies | |
| if: runner.os == 'Linux' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y \ | |
| libwebkit2gtk-4.1-dev \ | |
| libayatana-appindicator3-dev \ | |
| librsvg2-dev \ | |
| patchelf \ | |
| libfuse2 \ | |
| gstreamer1.0-plugins-base \ | |
| gstreamer1.0-plugins-good \ | |
| gstreamer1.0-plugins-bad \ | |
| gstreamer1.0-libav \ | |
| gstreamer1.0-pipewire | |
| - name: Guard — refuse to release with placeholder updater pubkey | |
| shell: bash | |
| run: | | |
| if grep -q 'REPLACE_WITH_TAURI_UPDATER_PUBKEY' "$TAURI_APP_PATH/src-tauri/tauri.conf.json"; then | |
| echo "::error::tauri.conf.json still has the placeholder updater pubkey." | |
| echo "::error::Generate a keypair with 'pnpm exec tauri signer generate' and commit the public key." | |
| echo "::error::See templates/clips/desktop/README.md for the full setup." | |
| exit 1 | |
| fi | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable | |
| with: | |
| targets: ${{ matrix.rust-targets }} | |
| - name: Link Apple compiler runtime for Rust | |
| if: runner.os == 'macOS' | |
| shell: bash | |
| run: | | |
| # Xcode emits __isPlatformVersionAtLeast for whisper-rs-sys's | |
| # availability checks. Rust's final link does not add compiler-rt. | |
| CLANG_RUNTIME="$(xcrun --sdk macosx clang -print-libgcc-file-name)" | |
| echo "RUSTFLAGS=${RUSTFLAGS:-} -C link-arg=${CLANG_RUNTIME}" >> "$GITHUB_ENV" | |
| - name: Rust cache | |
| uses: swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 | |
| with: | |
| workspaces: ${{ env.TAURI_APP_PATH }}/src-tauri | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Auto-bump version (push trigger only) | |
| if: github.event_name == 'push' | |
| working-directory: ${{ env.TAURI_APP_PATH }} | |
| shell: bash | |
| run: | | |
| CURRENT=$(node -p "require('./package.json').version") | |
| # Use major.minor from package.json + run_number as patch for | |
| # guaranteed uniqueness without committing version bumps back. | |
| NEXT=$(node -e "const [ma,mi]=process.argv[1].split('.'); console.log(ma+'.'+mi+'.${{ github.run_number }}')" "$CURRENT") | |
| npm version "$NEXT" --no-git-tag-version | |
| node -e " | |
| const fs = require('fs'); | |
| const tc = JSON.parse(fs.readFileSync('src-tauri/tauri.conf.json','utf8')); | |
| tc.version = '$NEXT'; | |
| fs.writeFileSync('src-tauri/tauri.conf.json', JSON.stringify(tc, null, 2) + '\n'); | |
| const cargo = fs.readFileSync('src-tauri/Cargo.toml','utf8'); | |
| fs.writeFileSync('src-tauri/Cargo.toml', cargo.replace(/^version = \".*\"/m, 'version = \"$NEXT\"')); | |
| " | |
| echo "Auto-bumped version: $CURRENT → $NEXT" | |
| - name: Bump version (manual) | |
| if: inputs.version != '' | |
| working-directory: ${{ env.TAURI_APP_PATH }} | |
| shell: bash | |
| env: | |
| RELEASE_VERSION: ${{ inputs.version }} | |
| run: | | |
| npm version "$RELEASE_VERSION" --no-git-tag-version | |
| node -e " | |
| const fs = require('fs'); | |
| const tc = JSON.parse(fs.readFileSync('src-tauri/tauri.conf.json','utf8')); | |
| tc.version = '$RELEASE_VERSION'; | |
| fs.writeFileSync('src-tauri/tauri.conf.json', JSON.stringify(tc, null, 2) + '\n'); | |
| const cargo = fs.readFileSync('src-tauri/Cargo.toml','utf8'); | |
| fs.writeFileSync('src-tauri/Cargo.toml', cargo.replace(/^version = \".*\"/m, 'version = \"$RELEASE_VERSION\"')); | |
| " | |
| - name: Resolve version | |
| id: resolve-version | |
| shell: bash | |
| run: | | |
| V=$(node -p "require('./${{ env.TAURI_APP_PATH }}/package.json').version") | |
| echo "version=$V" >> "$GITHUB_OUTPUT" | |
| - name: Build and release (tauri-action) | |
| uses: tauri-apps/tauri-action@84b9d35b5fc46c1e45415bdb6144030364f7ebc5 # v0 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # Tauri signing — REQUIRED for the updater to accept downloaded | |
| # bundles. See templates/clips/desktop/README.md for key setup. | |
| TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.CLIPS_TAURI_SIGNING_PRIVATE_KEY }} | |
| TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.CLIPS_TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} | |
| # macOS codesign + notarize (reuses the Electron app's Apple creds). | |
| APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} | |
| APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} | |
| APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} | |
| APPLE_ID: ${{ secrets.APPLE_ID }} | |
| APPLE_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} | |
| APPLE_TEAM_ID: W3PMF2T3MW | |
| # Sentry error reporting for the Tauri native process and webview. | |
| CLIPS_DESKTOP_SENTRY_DSN: ${{ secrets.CLIPS_DESKTOP_SENTRY_DSN || secrets.SENTRY_CLIENT_DSN || secrets.SENTRY_DSN }} | |
| CLIPS_DESKTOP_SENTRY_CLIENT_KEY: ${{ secrets.CLIPS_DESKTOP_SENTRY_CLIENT_KEY || secrets.SENTRY_CLIENT_KEY }} | |
| CLIPS_DESKTOP_SENTRY_PROJECT_ID: ${{ secrets.CLIPS_DESKTOP_SENTRY_PROJECT_ID || secrets.SENTRY_PROJECT_ID }} | |
| CLIPS_DESKTOP_SENTRY_INGEST_HOST: ${{ secrets.CLIPS_DESKTOP_SENTRY_INGEST_HOST || secrets.SENTRY_INGEST_HOST }} | |
| CLIPS_DESKTOP_SENTRY_ENVIRONMENT: production | |
| with: | |
| projectPath: ${{ env.TAURI_APP_PATH }} | |
| tagName: clips-v__VERSION__ | |
| releaseName: "Clips Desktop v__VERSION__" | |
| releaseBody: | | |
| Auto-updating build of the Clips menu-bar app. | |
| The in-app updater fetches its manifest from the `clips-latest` | |
| release — this versioned release is the immutable source of the | |
| signed bundles it downloads. | |
| releaseDraft: true | |
| prerelease: false | |
| args: ${{ matrix.args }} | |
| - name: Verify macOS permission metadata | |
| if: runner.os == 'macOS' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| APP_PATH=$(find "$TAURI_APP_PATH/src-tauri/target" -path '*/bundle/macos/Clips.app' -type d | head -n 1) | |
| if [ -z "$APP_PATH" ]; then | |
| echo "::error::Could not find built Clips.app bundle" | |
| exit 1 | |
| fi | |
| INFO="$APP_PATH/Contents/Info.plist" | |
| /usr/libexec/PlistBuddy -c "Print :NSCameraUsageDescription" "$INFO" >/dev/null | |
| /usr/libexec/PlistBuddy -c "Print :NSMicrophoneUsageDescription" "$INFO" >/dev/null | |
| /usr/libexec/PlistBuddy -c "Print :NSScreenCaptureUsageDescription" "$INFO" >/dev/null | |
| /usr/libexec/PlistBuddy -c "Print :NSCalendarsUsageDescription" "$INFO" >/dev/null | |
| /usr/libexec/PlistBuddy -c "Print :NSCalendarsFullAccessUsageDescription" "$INFO" >/dev/null | |
| /usr/libexec/PlistBuddy -c "Print :NSSpeechRecognitionUsageDescription" "$INFO" >/dev/null | |
| /usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "$INFO" | grep -qx 'com.clips.tray' | |
| /usr/libexec/PlistBuddy -c "Print :CFBundleExecutable" "$INFO" | grep -qx 'Clips' | |
| VERSION=$(/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" "$INFO") | |
| [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.-].*)?$ ]] | |
| /usr/libexec/PlistBuddy -c "Print :CFBundleVersion" "$INFO" >/dev/null | |
| ENTITLEMENTS=$(codesign -d --entitlements :- "$APP_PATH" 2>/dev/null) | |
| grep -q '<key>com.apple.security.device.camera</key>' <<< "$ENTITLEMENTS" | |
| grep -q '<key>com.apple.security.device.audio-input</key>' <<< "$ENTITLEMENTS" | |
| codesign --verify --deep --strict --verbose=2 "$APP_PATH" | |
| publish-release: | |
| needs: build-tauri | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| - name: Resolve version | |
| id: v | |
| run: echo "version=${{ needs.build-tauri.outputs.app-version }}" >> "$GITHUB_OUTPUT" | |
| - name: Mark versioned release as published | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| retry() { | |
| local attempts="$1" | |
| shift | |
| local delay=2 | |
| local n=1 | |
| until "$@"; do | |
| if [ "$n" -ge "$attempts" ]; then | |
| return 1 | |
| fi | |
| echo "Attempt $n failed; retrying in ${delay}s..." | |
| sleep "$delay" | |
| n=$((n + 1)) | |
| delay=$((delay * 2)) | |
| done | |
| } | |
| retry 5 gh release edit "clips-v${{ steps.v.outputs.version }}" \ | |
| --draft=false \ | |
| --latest=false | |
| - name: Download and publish updater manifest | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| retry() { | |
| local attempts="$1" | |
| shift | |
| local delay=2 | |
| local n=1 | |
| until "$@"; do | |
| if [ "$n" -ge "$attempts" ]; then | |
| return 1 | |
| fi | |
| echo "Attempt $n failed; retrying in ${delay}s..." | |
| sleep "$delay" | |
| n=$((n + 1)) | |
| delay=$((delay * 2)) | |
| done | |
| } | |
| RELEASE_TAG="clips-v${{ steps.v.outputs.version }}" | |
| mkdir -p manifest | |
| # latest.json is only produced when TAURI_SIGNING_PRIVATE_KEY is set. | |
| # Check explicitly so real API/auth errors aren't silently swallowed. | |
| HAS_MANIFEST=$(gh release view "$RELEASE_TAG" --json assets \ | |
| -q '[.assets[].name] | any(. == "latest.json")') || { | |
| echo "❌ Failed to query release assets for $RELEASE_TAG" | |
| exit 1 | |
| } | |
| if [ "$HAS_MANIFEST" = "true" ]; then | |
| retry 5 gh release download "$RELEASE_TAG" --pattern 'latest.json' --dir manifest | |
| mv manifest/latest.json manifest/clips-latest.json | |
| MANIFEST_TITLE="Clips Desktop — latest (updater manifest)" | |
| MANIFEST_NOTES="Stable URL for the Clips auto-updater. Overwritten on every release. Versioned bundles live in clips-v* releases." | |
| if gh release view clips-latest --json tagName -q .tagName >/dev/null 2>&1; then | |
| retry 5 gh release delete-asset clips-latest clips-latest.json --yes || true | |
| retry 5 gh release upload clips-latest manifest/clips-latest.json | |
| retry 5 gh release edit clips-latest \ | |
| --title "$MANIFEST_TITLE" \ | |
| --notes "$MANIFEST_NOTES" | |
| else | |
| retry 5 gh release create clips-latest manifest/clips-latest.json \ | |
| --title "$MANIFEST_TITLE" \ | |
| --notes "$MANIFEST_NOTES" \ | |
| --latest=false | |
| fi | |
| echo "✅ Updater manifest published to clips-latest release" | |
| else | |
| echo "⚠️ No latest.json in $RELEASE_TAG — TAURI_SIGNING_PRIVATE_KEY may not be configured." | |
| echo "⚠️ Release published without auto-update support. Users can still download manually." | |
| fi |