Skip to content

Mic instantly stop when try to use any Input Method (Microphone) other than System Default #44

Mic instantly stop when try to use any Input Method (Microphone) other than System Default

Mic instantly stop when try to use any Input Method (Microphone) other than System Default #44

Workflow file for this run

name: PR Build
# Triggered by commenting "/build" or "/build-quick" on a pull request.
# Builds a signed DMG (notarized if /build) from the PR branch and posts
# a download link as a comment on the PR.
on:
issue_comment:
types: [created]
permissions:
contents: read
pull-requests: write
actions: read
concurrency:
# One build per PR — re-commenting /build cancels any in-progress build
group: pr-build-${{ github.event.issue.number }}
cancel-in-progress: true
jobs:
pr-build:
name: Build PR
# Only run when:
# 1. The comment is on a pull request (not a plain issue)
# 2. The comment body is exactly "/build" (trimmed)
if: >-
github.event.issue.pull_request &&
(contains(github.event.comment.body, '/build') || contains(github.event.comment.body, '/build-quick'))
runs-on: macos-15
timeout-minutes: 45
steps:
# ── Setup ─────────────────────────────────────────────────────────────
- name: React to comment
uses: actions/github-script@v7
with:
script: |
await github.rest.reactions.createForIssueComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: context.payload.comment.id,
content: 'rocket'
});
- name: Get PR branch info
id: pr
uses: actions/github-script@v7
with:
script: |
const pr = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number
});
core.setOutput('ref', pr.data.head.ref);
core.setOutput('sha', pr.data.head.sha);
core.setOutput('short_sha', pr.data.head.sha.substring(0, 7));
core.setOutput('pr_number', context.issue.number);
core.setOutput('pr_title', pr.data.title);
- name: Determine build type
id: build_type
run: |
if [[ "${{ github.event.comment.body }}" == *"/build-quick"* ]]; then
echo "QUICK=true" >> "$GITHUB_OUTPUT"
echo "BUILD_DESCRIPTION=Build signed DMG (skipping notarization)" >> "$GITHUB_OUTPUT"
echo "DIST_FLAGS=--skip-notarize" >> "$GITHUB_OUTPUT"
else
echo "QUICK=false" >> "$GITHUB_OUTPUT"
echo "BUILD_DESCRIPTION=Build signed & notarized DMG" >> "$GITHUB_OUTPUT"
echo "DIST_FLAGS=" >> "$GITHUB_OUTPUT"
fi
- name: Post "build started" comment
uses: actions/github-script@v7
with:
script: |
const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
const description = "${{ steps.build_type.outputs.BUILD_DESCRIPTION }}";
const timeEstimate = "${{ steps.build_type.outputs.QUICK }}" === "true" ? "3–5 minutes" : "10–20 minutes";
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: `⏳ **PR Build started** for \`${{ steps.pr.outputs.short_sha }}\`\n\n${description}... this usually takes ${timeEstimate}.\n\n[Watch the build →](${runUrl})`
});
- name: Checkout PR branch
uses: actions/checkout@v6
with:
ref: ${{ steps.pr.outputs.ref }}
- name: Cache SPM dependencies
uses: actions/cache@v5
with:
path: .build
key: spm-${{ runner.os }}-${{ hashFiles('Package.resolved') }}
restore-keys: |
spm-${{ runner.os }}-
# ── Test ──────────────────────────────────────────────────────────────
- name: Run tests
run: swift test
- name: Initialize Xcode tools
run: sudo xcodebuild -runFirstLaunch
# ── Code Signing Setup ──────────────────────────────────────────────────
- name: Import Developer ID certificate
env:
DEVELOPER_ID_CERT_P12: ${{ secrets.DEVELOPER_ID_CERT_P12 }}
DEVELOPER_ID_CERT_PASSWORD: ${{ secrets.DEVELOPER_ID_CERT_PASSWORD }}
run: |
KEYCHAIN_PATH="$RUNNER_TEMP/build.keychain"
KEYCHAIN_PASSWORD="$(openssl rand -hex 16)"
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
echo "$DEVELOPER_ID_CERT_P12" | base64 --decode > "$RUNNER_TEMP/cert.p12"
security import "$RUNNER_TEMP/cert.p12" \
-k "$KEYCHAIN_PATH" \
-P "$DEVELOPER_ID_CERT_PASSWORD" \
-T /usr/bin/codesign \
-T /usr/bin/security
security set-key-partition-list \
-S apple-tool:,apple:,codesign: \
-s -k "$KEYCHAIN_PASSWORD" \
"$KEYCHAIN_PATH"
security list-keychains -d user -s "$KEYCHAIN_PATH" $(security list-keychains -d user | xargs)
echo "KEYCHAIN_PATH=$KEYCHAIN_PATH" >> "$GITHUB_ENV"
echo "KEYCHAIN_PASSWORD=$KEYCHAIN_PASSWORD" >> "$GITHUB_ENV"
IDENTITY=$(security find-identity -v -p codesigning "$KEYCHAIN_PATH" \
| grep "Developer ID Application" | head -1 | sed 's/.*"\(.*\)"/\1/')
if [ -z "$IDENTITY" ]; then
echo "❌ Developer ID certificate not found after import"
exit 1
fi
echo "✅ Imported: $IDENTITY"
echo "CODE_SIGN_IDENTITY=$IDENTITY" >> "$GITHUB_ENV"
- name: Store notarization credentials
if: steps.build_type.outputs.QUICK != 'true'
env:
NOTARIZE_APPLE_ID: ${{ secrets.NOTARIZE_APPLE_ID }}
NOTARIZE_TEAM_ID: ${{ secrets.NOTARIZE_TEAM_ID }}
NOTARIZE_PASSWORD: ${{ secrets.NOTARIZE_PASSWORD }}
run: |
xcrun notarytool store-credentials "AC_PASSWORD" \
--apple-id "$NOTARIZE_APPLE_ID" \
--team-id "$NOTARIZE_TEAM_ID" \
--password "$NOTARIZE_PASSWORD"
# ── Build & Package ─────────────────────────────────────────────────────
- name: Set PR build version
run: |
# Tag the build so it's identifiable (e.g., 0.6.2-pr.106+abc1234)
BASE_VERSION="${APP_VERSION:-}"
if [ -z "$BASE_VERSION" ]; then
BASE_VERSION="$(grep '^APP_VERSION=' scripts/build.sh | sed 's/.*:-\(.*\)}.*/\1/' | head -1 || true)"
fi
if [ -z "$BASE_VERSION" ]; then
echo "❌ Unable to determine base app version from scripts/build.sh" >&2
exit 1
fi
PR_VERSION="${BASE_VERSION}-pr.${{ steps.pr.outputs.pr_number }}+${{ steps.pr.outputs.short_sha }}"
echo "APP_VERSION=$PR_VERSION" >> "$GITHUB_ENV"
echo "📦 Build version: $PR_VERSION"
- name: Build signed DMG
run: ./scripts/dist.sh ${{ steps.build_type.outputs.DIST_FLAGS }}
- name: Verify code signature
run: codesign -v --deep --strict VocaMac.app
- name: Verify SPM resource bundles are correctly staged
# Mirrors the validation in release.yml and nightly.yml so PR builds
# catch bundle-layout regressions (the #119 class of bug) before they
# reach a release. xcodebuild's Bundle.module accessor resolves to
# Contents/Resources/, and bundles must NOT live at the .app root
# (codesign will reject that layout).
run: |
echo "🔍 Checking SPM resource bundle layout..."
FAIL=0
# Bundles must NOT be at the .app root (codesign rejects that).
if ls VocaMac.app/*.bundle 1>/dev/null 2>&1; then
echo "❌ Found .bundle at app root — will break codesign!"
ls VocaMac.app/*.bundle
FAIL=1
fi
FOUND_ANY=false
for bundle in VocaMac.app/Contents/Resources/*.bundle; do
[ -d "$bundle" ] || continue
FOUND_ANY=true
name="$(basename "$bundle")"
echo "✅ $name — present in Contents/Resources/"
done
if [ "$FOUND_ANY" = false ]; then
echo "❌ No .bundle directories found in Contents/Resources/!"
FAIL=1
fi
# Verify the critical WhisperKit tokenizer fallback resources.
# xcodebuild bundles have the structure: <bundle>/Contents/Resources/<file>
HUB="VocaMac.app/Contents/Resources/swift-transformers_Hub.bundle"
if [ ! -d "$HUB" ]; then
echo "❌ swift-transformers_Hub.bundle missing!"
FAIL=1
else
for f in gpt2_tokenizer_config.json t5_tokenizer_config.json; do
if find "$HUB" -name "$f" -print -quit | grep -q .; then
echo "✅ $f present in swift-transformers_Hub.bundle"
else
echo "❌ Missing $f in swift-transformers_Hub.bundle"
FAIL=1
fi
done
fi
# Verify the binary uses the xcodebuild accessor (contains resourceURL)
if strings VocaMac.app/Contents/MacOS/VocaMac | grep -q 'resourceURL'; then
echo "✅ Binary uses xcodebuild Bundle.module accessor"
else
echo "❌ Binary does NOT contain resourceURL — wrong accessor!"
echo " Ensure build.sh uses xcodebuild, not swift build."
FAIL=1
fi
if [ "$FAIL" -ne 0 ]; then
echo ""
echo "Bundle layout validation failed. See above for details."
exit 1
fi
echo ""
echo "✅ All bundle checks passed."
- name: Gather artifacts
id: artifacts
run: |
DMG_FILE=$(ls dist/VocaMac-*.dmg | head -1)
DMG_NAME=$(basename "$DMG_FILE")
DMG_SIZE=$(du -h "$DMG_FILE" | cut -f1)
DMG_SHA=$(shasum -a 256 "$DMG_FILE" | awk '{print $1}')
echo "dmg_path=$DMG_FILE" >> "$GITHUB_OUTPUT"
echo "dmg_name=$DMG_NAME" >> "$GITHUB_OUTPUT"
echo "dmg_size=$DMG_SIZE" >> "$GITHUB_OUTPUT"
echo "dmg_sha=$DMG_SHA" >> "$GITHUB_OUTPUT"
# ── Upload & Notify ─────────────────────────────────────────────────────
- name: Upload DMG artifact
uses: actions/upload-artifact@v7
id: upload
with:
name: ${{ steps.artifacts.outputs.dmg_name }}
path: ${{ steps.artifacts.outputs.dmg_path }}
retention-days: 14
- name: Post download comment on PR
uses: actions/github-script@v7
with:
script: |
const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
const artifactUrl = `${runUrl}/artifacts/${{ steps.upload.outputs.artifact-id }}`;
const isQuick = "${{ steps.build_type.outputs.QUICK }}" === "true";
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: [
`✅ **PR Build ready!**`,
``,
`| | |`,
`|---|---|`,
`| **DMG** | [\`${{ steps.artifacts.outputs.dmg_name }}\`](${artifactUrl}) |`,
`| **Size** | ${{ steps.artifacts.outputs.dmg_size }} |`,
`| **Branch** | \`${{ steps.pr.outputs.ref }}\` |`,
`| **Commit** | \`${{ steps.pr.outputs.short_sha }}\` |`,
`| **Signed** | ✅ Developer ID |`,
`| **Notarized** | ${isQuick ? '⏭️ Skipped' : '✅ Apple'} |`,
``,
`### 📥 Install`,
`1. Click the DMG link above to download`,
`2. Open the DMG and drag VocaMac to Applications (replace existing)`,
`3. Open VocaMac — ${isQuick ? 'Right-click → Open once to bypass Gatekeeper' : 'No Gatekeeper warnings'}, no permission resets`,
``,
`<details><summary>SHA-256 checksum</summary>`,
``,
`\`\`\``,
`${{ steps.artifacts.outputs.dmg_sha }} ${{ steps.artifacts.outputs.dmg_name }}`,
`\`\`\``,
`</details>`,
``,
`> 💡 Comment \`/build\` or \`/build-quick\` to rebuild.`
].join('\n')
});
# ── Cleanup ─────────────────────────────────────────────────────────────
- name: Delete temporary keychain
if: always()
run: |
security delete-keychain "$KEYCHAIN_PATH" 2>/dev/null || true
# ── Failure notification ────────────────────────────────────────────────
- name: Post failure comment
if: failure()
uses: actions/github-script@v7
with:
script: |
const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: `❌ **PR Build failed** for \`${{ steps.pr.outputs.short_sha }}\`\n\n[View build logs →](${runUrl})\n\n> 💡 Fix the issue and comment \`/build\` to retry.`
});