Skip to content

fix(build): sign all Mach-O binaries inside non-standard framework bundles#1254

Merged
ErikBjare merged 1 commit intoActivityWatch:masterfrom
TimeToBuildBob:fix/sign-all-mach-o-in-framework-fallback
Apr 9, 2026
Merged

fix(build): sign all Mach-O binaries inside non-standard framework bundles#1254
ErikBjare merged 1 commit intoActivityWatch:masterfrom
TimeToBuildBob:fix/sign-all-mach-o-in-framework-fallback

Conversation

@TimeToBuildBob
Copy link
Copy Markdown
Contributor

Problem

After #1250 and #1249, the fallback for Python.framework in the inside-out signing loop signs only a single binary ($fw_name → e.g. Python.framework/Python).

PyInstaller copies Python.framework as separate files (not symlinks), so there are actually three distinct Mach-O binaries:

  • Python.framework/Python
  • Python.framework/Versions/Current/Python
  • Python.framework/Versions/3.9/Python

Signing only the first leaves the other two with no signature + no secure timestamp — causing Apple notarization to reject all three watcher bundles with ~18 errors total, and master Build Tauri CI to fail with status: Invalid.

Fix

Replace the single-binary fallback with a loop that finds all Mach-O files inside the framework dir:

find "$fw" -type f | xargs file | grep "Mach-O" | cut -d: -f1

Each binary is signed via a temp-path copy (to avoid codesign's bundle format is ambiguous error when the parent dir is .framework).

Validation

This exact fix was validated on CI in the (now-closed) PR #1252 branch (3e635e42):

  • ✅ macOS-14 Build Tauri
  • ✅ macOS-latest Build Tauri (including notarization — status: Accepted)
  • ✅ ubuntu-24.04
  • ✅ ubuntu-24.04-arm
  • ✅ windows-latest

Shell syntax validated locally: bash -n scripts/package/build_app_tauri.sh

…ndles

PyInstaller copies Python.framework contents as separate files rather than
symlinks, so Python, Versions/Current/Python, and Versions/3.9/Python are
distinct inodes. The previous fallback only signed the single $fw_name binary,
leaving the Versions/ copies unsigned — causing Apple notarization to reject
all three affected watcher bundles (~6 errors per watcher, ~18 total).

Replace the single-binary fallback with a loop that finds all Mach-O files
inside the framework via `find -type f | xargs file | grep Mach-O` and signs
each via a temp-path copy (avoids the in-place "bundle format is ambiguous"
error from codesign when the parent dir is a .framework).

This fix was validated in CI on the ActivityWatch#1252 PR branch (3e635e4): all platforms
passed including both macOS Build Tauri jobs with notarization succeeding.
@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Apr 9, 2026

Greptile Summary

This PR fixes macOS notarization failures by replacing the single-binary fallback signing path for non-standard .framework bundles (introduced in #1249/#1250) with a loop that finds and signs all Mach-O binaries inside the framework via a temp-path copy. The root cause was that PyInstaller copies Python.framework as separate files (Python, Versions/Current/Python, Versions/3.9/Python) rather than symlinks, leaving two of the three binaries unsigned and causing Apple notarization to reject ~18 errors across watcher bundles.

Confidence Score: 5/5

Safe to merge — the fix is correct, CI-validated, and introduces no new risk.

All three previously-unsigned Mach-O copies inside the PyInstaller-copied Python.framework are now signed. The implementation uses the same find … | xargs file | grep Mach-O | cut -d: -f1 pattern already battle-tested in Step 1. Error handling (temp-file cleanup on failure, signed_count zero-guard) is solid. No P0/P1 issues found; the PR was validated on CI including notarization acceptance.

No files require special attention.

Vulnerabilities

No security concerns identified. The change deals exclusively with code-signing Mach-O binaries using Apple's codesign tool; it does not handle untrusted input, secrets, or network data.

Important Files Changed

Filename Overview
scripts/package/build_app_tauri.sh Replaces the single-binary ambiguous-bundle fallback with a `find …

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["Step 2: iterate .framework dirs\n(deepest first)"] --> B["codesign --force … fw"]
    B -- success --> C["echo Signed bundle: fw"]
    B -- failure --> D{"sign_output contains\n'bundle format is ambiguous'?"}
    D -- no --> E["ERROR: Failed to sign fw\nexit 1"]
    D -- yes --> F["find fw -type f\n| xargs file | grep Mach-O\n| cut -d: -f1"]
    F --> G["while read fw_bin"]
    G --> H["mktemp → tmp_binary\ncp fw_bin → tmp_binary"]
    H --> I["sign_binary tmp_binary"]
    I -- failure --> J["rm tmp_binary\nexit 1"]
    I -- success --> K["cp tmp_binary → fw_bin\nrm tmp_binary\nsigned_count++"]
    K --> G
    G -- loop done --> L{"signed_count == 0?"}
    L -- yes --> M["ERROR: No Mach-O found\nexit 1"]
    L -- no --> N["echo Signed N binaries inside fw"]
Loading

Reviews (1): Last reviewed commit: "fix(build): sign all Mach-O binaries ins..." | Re-trigger Greptile

@ErikBjare ErikBjare merged commit 0bfc97f into ActivityWatch:master Apr 9, 2026
15 checks passed
TimeToBuildBob added a commit to TimeToBuildBob/activitywatch that referenced this pull request Apr 9, 2026
…ndles (ActivityWatch#1254)

PyInstaller copies Python.framework contents as separate files rather than
symlinks, so Python, Versions/Current/Python, and Versions/3.9/Python are
distinct inodes. The previous fallback only signed the single $fw_name binary,
leaving the Versions/ copies unsigned — causing Apple notarization to reject
all three affected watcher bundles (~6 errors per watcher, ~18 total).

Replace the single-binary fallback with a loop that finds all Mach-O files
inside the framework via `find -type f | xargs file | grep Mach-O` and signs
each via a temp-path copy (avoids the in-place "bundle format is ambiguous"
error from codesign when the parent dir is a .framework).

This fix was validated in CI on the ActivityWatch#1252 PR branch (3e635e4): all platforms
passed including both macOS Build Tauri jobs with notarization succeeding.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants