fix: note race condition (#1277) #2
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: Build and Notarize | |
| on: | |
| push: | |
| branches: [main, develop] | |
| paths-ignore: | |
| - "**.md" | |
| - "LICENSE" | |
| - ".github/ISSUE_TEMPLATE/**" | |
| pull_request: | |
| branches: [main] | |
| paths-ignore: | |
| - "**.md" | |
| - "LICENSE" | |
| - ".github/ISSUE_TEMPLATE/**" | |
| workflow_dispatch: | |
| jobs: | |
| tests: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version-file: .nvmrc | |
| - name: Install dependencies | |
| run: npm ci --ignore-scripts | |
| - name: Run tests | |
| run: npm test | |
| env: | |
| # keeps require("electron") from throwing when postinstall was skipped | |
| ELECTRON_OVERRIDE_DIST_PATH: /tmp | |
| build-linux: | |
| needs: tests | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "24" | |
| cache: "npm" | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Install platform-specific native binaries | |
| run: npm install @rollup/rollup-linux-x64-gnu lightningcss-linux-x64-gnu @tailwindcss/oxide-linux-x64-gnu --no-save | |
| - name: Cache native binaries | |
| uses: actions/cache@v4 | |
| with: | |
| path: resources/bin | |
| key: linux-x64-bin-${{ hashFiles('scripts/download-*.js') }} | |
| - name: Download whisper.cpp binaries | |
| run: node scripts/download-whisper-cpp.js --current | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GH_TOKEN || github.token }} | |
| - name: Download llama-server binaries | |
| run: node scripts/download-llama-server.js --current | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GH_TOKEN || github.token }} | |
| - name: Download sherpa-onnx binaries | |
| run: node scripts/download-sherpa-onnx.js --current | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GH_TOKEN || github.token }} | |
| - name: Download yt-dlp binary | |
| run: node scripts/download-yt-dlp.js --current | |
| - name: Download Qdrant binary | |
| run: node scripts/download-qdrant.js --current | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GH_TOKEN || github.token }} | |
| - name: Download meeting AEC helper | |
| run: node scripts/download-meeting-aec-helper.js --current | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GH_TOKEN || github.token }} | |
| - name: Cache Electron | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cache/electron | |
| key: linux-electron-${{ hashFiles('**/package-lock.json') }} | |
| restore-keys: linux-electron- | |
| - name: Install Linux build and packaging dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y rpm libx11-dev libxtst-dev libatspi2.0-dev libglib2.0-dev libpipewire-0.3-dev pkg-config | |
| - name: Create runtime env | |
| run: | | |
| echo "GOOGLE_CALENDAR_CLIENT_ID=${{ secrets.GOOGLE_CALENDAR_CLIENT_ID }}" >> .env | |
| echo "GOOGLE_CALENDAR_CLIENT_SECRET=${{ secrets.GOOGLE_CALENDAR_CLIENT_SECRET }}" >> .env | |
| - name: Verify PipeWire system audio helper build | |
| run: | | |
| npm run compile:linux-system-audio | |
| # supportsNativeCapture reflects compile-time PipeWire linkage, so this passes on CI runners with no PipeWire daemon (where "ok" is false). | |
| resources/bin/linux-system-audio-helper probe | grep '"supportsNativeCapture":true' | |
| - name: Build Application | |
| run: npm run build:linux -- --publish never | |
| env: | |
| GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }} | |
| VITE_NEON_AUTH_URL: ${{ vars.VITE_NEON_AUTH_URL }} | |
| VITE_OPENWHISPR_API_URL: ${{ vars.VITE_OPENWHISPR_API_URL }} | |
| VITE_OPENWHISPR_OAUTH_CALLBACK_URL: ${{ vars.VITE_OPENWHISPR_OAUTH_CALLBACK_URL }} | |
| - name: Upload Artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: linux-build | |
| path: dist/ | |
| retention-days: 7 | |
| build-windows: | |
| needs: tests | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "24" | |
| cache: "npm" | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Install platform-specific native binaries | |
| run: npm install @rollup/rollup-win32-x64-msvc lightningcss-win32-x64-msvc @tailwindcss/oxide-win32-x64-msvc --no-save | |
| - name: Cache native binaries | |
| uses: actions/cache@v4 | |
| with: | |
| path: resources/bin | |
| key: windows-x64-bin-${{ hashFiles('scripts/download-*.js') }} | |
| - name: Download whisper.cpp binaries | |
| run: node scripts/download-whisper-cpp.js --current | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GH_TOKEN || github.token }} | |
| - name: Download llama-server binaries | |
| run: node scripts/download-llama-server.js --current | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GH_TOKEN || github.token }} | |
| - name: Download sherpa-onnx binaries | |
| run: node scripts/download-sherpa-onnx.js --current | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GH_TOKEN || github.token }} | |
| - name: Download yt-dlp binary | |
| run: node scripts/download-yt-dlp.js --current | |
| - name: Download Qdrant binary | |
| run: node scripts/download-qdrant.js --current | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GH_TOKEN || github.token }} | |
| - name: Download meeting AEC helper | |
| run: node scripts/download-meeting-aec-helper.js --current | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GH_TOKEN || github.token }} | |
| - name: Download nircmd.exe | |
| run: node scripts/download-nircmd.js | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GH_TOKEN || github.token }} | |
| - name: Download windows-fast-paste.exe | |
| run: node scripts/download-windows-fast-paste.js | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GH_TOKEN || github.token }} | |
| - name: Download windows-key-listener.exe | |
| run: node scripts/download-windows-key-listener.js | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GH_TOKEN || github.token }} | |
| - name: Download windows-mic-listener.exe | |
| run: node scripts/download-windows-mic-listener.js | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GH_TOKEN || github.token }} | |
| - name: Cache Electron | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/AppData/Local/electron/Cache | |
| key: windows-electron-${{ hashFiles('**/package-lock.json') }} | |
| restore-keys: windows-electron- | |
| - name: Create runtime env | |
| shell: bash | |
| run: | | |
| echo "GOOGLE_CALENDAR_CLIENT_ID=${{ secrets.GOOGLE_CALENDAR_CLIENT_ID }}" >> .env | |
| echo "GOOGLE_CALENDAR_CLIENT_SECRET=${{ secrets.GOOGLE_CALENDAR_CLIENT_SECRET }}" >> .env | |
| - name: Build and Sign Application | |
| run: | | |
| if [ "${{ github.event_name }}" = "pull_request" ]; then | |
| npm run build:win -- --publish never --config electron-builder.unsigned-win.json | |
| else | |
| npm run build:win -- --publish never | |
| fi | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }} | |
| VITE_NEON_AUTH_URL: ${{ vars.VITE_NEON_AUTH_URL }} | |
| VITE_OPENWHISPR_API_URL: ${{ vars.VITE_OPENWHISPR_API_URL }} | |
| VITE_OPENWHISPR_OAUTH_CALLBACK_URL: ${{ vars.VITE_OPENWHISPR_OAUTH_CALLBACK_URL }} | |
| AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} | |
| AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} | |
| AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} | |
| - name: Upload Artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: windows-build | |
| path: dist/ | |
| retention-days: 7 | |
| build-macos: | |
| needs: tests | |
| runs-on: macos-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| arch: [x64, arm64] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "24" | |
| cache: "npm" | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Install ffmpeg-static for target architecture | |
| if: matrix.arch == 'x64' | |
| run: | | |
| rm -f node_modules/ffmpeg-static/ffmpeg | |
| npm_config_arch=x64 node node_modules/ffmpeg-static/install.js | |
| - name: Cache native binaries | |
| uses: actions/cache@v4 | |
| with: | |
| path: resources/bin | |
| key: macos-${{ matrix.arch }}-bin-${{ hashFiles('scripts/download-*.js', 'scripts/build-globe-listener.js', 'resources/macos-globe-listener.swift', 'scripts/build-macos-fast-paste.js', 'resources/macos-fast-paste.swift', 'scripts/build-media-remote.js', 'resources/macos-media-remote.swift', 'scripts/build-macos-audio-tap.js', 'resources/macos-audio-tap.swift') }} | |
| - name: Compile macos-globe-listener binary | |
| run: npm run compile:globe -- --arch ${{ matrix.arch }} | |
| - name: Compile macos-fast-paste binary | |
| run: npm run compile:fast-paste -- --arch ${{ matrix.arch }} | |
| - name: Compile macos-media-remote binary | |
| run: npm run compile:media-remote -- --arch ${{ matrix.arch }} | |
| - name: Compile macos-audio-tap binary | |
| run: npm run compile:audio-tap -- --arch ${{ matrix.arch }} | |
| - name: Download whisper.cpp binaries | |
| run: node scripts/download-whisper-cpp.js --current --platform darwin --arch ${{ matrix.arch }} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GH_TOKEN || github.token }} | |
| - name: Download llama-server binaries | |
| run: node scripts/download-llama-server.js --current --platform darwin --arch ${{ matrix.arch }} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GH_TOKEN || github.token }} | |
| - name: Download sherpa-onnx binaries | |
| run: node scripts/download-sherpa-onnx.js --current --platform darwin --arch ${{ matrix.arch }} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GH_TOKEN || github.token }} | |
| - name: Download yt-dlp binary | |
| run: node scripts/download-yt-dlp.js --current --platform darwin --arch ${{ matrix.arch }} | |
| - name: Download Qdrant binary | |
| run: node scripts/download-qdrant.js --current --platform darwin --arch ${{ matrix.arch }} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GH_TOKEN || github.token }} | |
| - name: Download meeting AEC helper | |
| run: node scripts/download-meeting-aec-helper.js --current --platform darwin --arch ${{ matrix.arch }} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GH_TOKEN || github.token }} | |
| - name: Cache Electron | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cache/electron | |
| key: macos-${{ matrix.arch }}-electron-${{ hashFiles('**/package-lock.json') }} | |
| restore-keys: macos-${{ matrix.arch }}-electron- | |
| - name: Setup macOS Code Signing | |
| if: github.event_name != 'pull_request' | |
| env: | |
| APPLE_CERTIFICATE_BASE64: ${{ secrets.APPLE_CERTIFICATE_BASE64 }} | |
| APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} | |
| APPLE_API_KEY_BASE64: ${{ secrets.APPLE_API_KEY_BASE64 }} | |
| run: | | |
| CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12 | |
| API_KEY_PATH=$RUNNER_TEMP/api_key.p8 | |
| KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db | |
| echo "$APPLE_CERTIFICATE_BASE64" | base64 --decode > $CERTIFICATE_PATH | |
| echo "$APPLE_API_KEY_BASE64" | base64 --decode > $API_KEY_PATH | |
| security create-keychain -p "runner" $KEYCHAIN_PATH | |
| security set-keychain-settings -lut 21600 $KEYCHAIN_PATH | |
| security unlock-keychain -p "runner" $KEYCHAIN_PATH | |
| security import $CERTIFICATE_PATH -P "$APPLE_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH | |
| security list-keychain -d user -s $KEYCHAIN_PATH | |
| security set-key-partition-list -S apple-tool:,apple: -s -k "runner" $KEYCHAIN_PATH | |
| echo "APPLE_API_KEY=$API_KEY_PATH" >> $GITHUB_ENV | |
| - name: Create runtime env | |
| run: | | |
| echo "GOOGLE_CALENDAR_CLIENT_ID=${{ secrets.GOOGLE_CALENDAR_CLIENT_ID }}" >> .env | |
| echo "GOOGLE_CALENDAR_CLIENT_SECRET=${{ secrets.GOOGLE_CALENDAR_CLIENT_SECRET }}" >> .env | |
| - name: Build Application (${{ matrix.arch }}) | |
| run: npm run build:mac -- --${{ matrix.arch }} --publish never | |
| env: | |
| TARGET_ARCH: ${{ matrix.arch }} | |
| GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }} | |
| VITE_NEON_AUTH_URL: ${{ vars.VITE_NEON_AUTH_URL }} | |
| VITE_OPENWHISPR_API_URL: ${{ vars.VITE_OPENWHISPR_API_URL }} | |
| VITE_OPENWHISPR_OAUTH_CALLBACK_URL: ${{ vars.VITE_OPENWHISPR_OAUTH_CALLBACK_URL }} | |
| CSC_IDENTITY_AUTO_DISCOVERY: ${{ github.event_name != 'pull_request' && 'true' || 'false' }} | |
| CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} | |
| APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} | |
| APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }} | |
| APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
| - name: Verify packaged macOS native binaries | |
| run: | | |
| APP_PATH=$(ls -d dist/mac*/OpenWhispr.app | head -n1) | |
| for bin in macos-audio-tap macos-globe-listener macos-fast-paste macos-mic-listener macos-text-monitor macos-media-remote; do | |
| test -x "$APP_PATH/Contents/Resources/bin/$bin" || { echo "Missing in bundle: $bin"; exit 1; } | |
| done | |
| test -x "$APP_PATH/Contents/Resources/bin/MediaRemoteAdapter.framework/Versions/A/MediaRemoteAdapter" || { echo "Missing in bundle: MediaRemoteAdapter.framework"; exit 1; } | |
| test -f "$APP_PATH/Contents/Resources/bin/mediaremote-adapter.pl" || { echo "Missing in bundle: mediaremote-adapter.pl"; exit 1; } | |
| - name: Upload Artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: macos-${{ matrix.arch }}-build | |
| path: dist/ | |
| retention-days: 7 | |
| - name: Cleanup Certificates | |
| if: always() && github.event_name != 'pull_request' | |
| run: | | |
| security delete-keychain $RUNNER_TEMP/app-signing.keychain-db || true | |
| rm -f $RUNNER_TEMP/build_certificate.p12 | |
| rm -f $RUNNER_TEMP/api_key.p8 |