Build, code-sign, notarize, and package the FFmpegWrapper desktop app for distribution. Use when the user asks to build the app, create a release, sign the binary, notarize with Apple, package for distribution, or create installers for macOS/Windows/Linux.
ffmpeg_app/
├── app_entry.py # PyInstaller entry point
├── pyinstaller-macos.spec # macOS bundle spec
├── entitlements.plist # Hardened runtime entitlements (network access)
├── resources/app_icon.icns # macOS app icon
├── src/ffmpeg_app/ # Application source
├── requirements.txt # Runtime deps: PySide6, openai
└── pyproject.toml # Project metadata and deps
pyinstaller and project deps installedDeveloper ID Application certificate in Keychainsigning-config.local.shBefore signing, read signing-config.local.sh to get CODESIGN_IDENTITY and NOTARIZE_PROFILE. If the file is missing, run security find-identity -v -p codesigning to find the Developer ID Application hash and ask the user for their notarization keychain profile name.
If there are duplicate certs with the same name, use the SHA-1 hash (from the config file) to disambiguate.
# 0. Load local signing config
source signing-config.local.sh
# 1. Clean and build
rm -rf dist/ build/
pyinstaller --clean -y pyinstaller-macos.spec
# 2. Sign with hardened runtime
cd dist
codesign --deep --force --options runtime \
--entitlements ../entitlements.plist \
--sign "$CODESIGN_IDENTITY" \
FFmpegWrapper.app
# 3. Verify signature
codesign --verify --deep --strict FFmpegWrapper.app
# 4. Zip for notarization
rm -f FFmpegWrapper.zip
ditto -c -k --keepParent FFmpegWrapper.app FFmpegWrapper.zip
# 5. Submit for notarization (takes 1-3 min)
xcrun notarytool submit FFmpegWrapper.zip \
--keychain-profile "$NOTARIZE_PROFILE" --wait
# 6. Staple the ticket
xcrun stapler staple FFmpegWrapper.app
# 7. Verify Gatekeeper
spctl -a -vv FFmpegWrapper.app
Expected final output: FFmpegWrapper.app: accepted, source=Notarized Developer ID
The zip from step 4 was created before stapling. If distributing a zip, re-create it:
cd dist
rm -f FFmpegWrapper.zip
ditto -c -k --keepParent FFmpegWrapper.app FFmpegWrapper.zip
signing-config.local.sh instead of the name string.xcrun notarytool log <submission-id> --keychain-profile "$NOTARIZE_PROFILE" to see the rejection details.hiddenimports in pyinstaller-macos.spec. Current list includes openai, httpx, pydantic, and their transitive deps.-y flag or rm -rf dist/ build/ before building..app bundle correctly. Will need migration to onedir in PyInstaller v7.entitlements.plist grants:
com.apple.security.cs.allow-unsigned-executable-memory -- required by Python/PyInstallercom.apple.security.network.client -- required for Deepgram and OpenAI API callsIf new entitlements are needed (e.g., camera, microphone), add them to entitlements.plist before signing.
When adding Windows support:
pyinstaller-windows.spec based on the macOS spec.icns icon with .ico in the specBUNDLE section (Windows uses the EXE directly)signtool if a code signing certificate is availableWhen adding Linux support:
pyinstaller-linux.spec based on the macOS specBUNDLE section and icon referencesdist/FFmpegWrapper.deb with dpkg-deb or .AppImage with appimagetool