Skip to content

fix(build): sign non-standard framework binary via temp copy to avoid ambiguity#1250

Merged
ErikBjare merged 1 commit intoActivityWatch:masterfrom
TimeToBuildBob:fix/python-framework-signing-workaround
Apr 9, 2026
Merged

fix(build): sign non-standard framework binary via temp copy to avoid ambiguity#1250
ErikBjare merged 1 commit intoActivityWatch:masterfrom
TimeToBuildBob:fix/python-framework-signing-workaround

Conversation

@TimeToBuildBob
Copy link
Copy Markdown
Contributor

Problem

After #1249 merged, master Build Tauri still fails with:

dist/ActivityWatch.app/Contents/Resources/aw-watcher-window/Python.framework/Python: bundle format is ambiguous (could be app or framework)
make: *** [dist/ActivityWatch.app] Error 1

#1249 added a fallback for the "bundle format is ambiguous" error in Step 2: when signing Python.framework as a bundle fails, it tries to sign the main binary (Python.framework/Python) directly via sign_binary.

But sign_binary calls codesign on the same path (Python.framework/Python), which still sees the .framework parent directory and reports the same "bundle format is ambiguous" error — same failure, one level deeper.

Fix

Instead of signing Python.framework/Python in-place, copy it to a temp path outside any .framework directory, sign it there, then copy the signed binary back.

Code signatures are embedded in the Mach-O binary (not path-dependent), so the result is identical. When the binary is at a temp path, codesign sees no .framework context and signs it successfully as a standalone executable.

Test plan

  • Post-merge master Build Tauri CI passes (macOS-14 and macOS-latest)
  • Package dmg step completes without "bundle format is ambiguous" errors

This should be the final fix needed to unblock the Thursday 2026-04-09 12:00 UTC scheduled Create dev release run.

… ambiguity

codesign refuses to sign Python.framework/Python in-place when the binary
is inside a .framework directory — it sees the directory context and reports
'bundle format is ambiguous (could be app or framework)'.

The ActivityWatch#1249 fallback correctly detected this case but then called sign_binary
on the same path, which hits the same codesign check.

Fix: copy the binary to a temp path outside any .framework dir, sign it
there, then copy the signed binary back. Code signatures are embedded in
the Mach-O binary (not path-dependent), so the result is identical.

This should be the final fix needed to unblock the Build Tauri master CI
and allow the Thursday 2026-04-09 12:00 UTC scheduled dev release to run.
@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Apr 8, 2026

Greptile Summary

This PR fixes the lingering "bundle format is ambiguous" codesign failure introduced after #1249 by copying the non-standard PyInstaller-embedded Python.framework/Python binary to a mktemp path outside any .framework directory, signing it there, then copying the signed binary back. Because code signatures are embedded in the Mach-O binary (not path-dependent), the result is identical to an in-place sign — codesign simply no longer sees a .framework parent and stops complaining.

Confidence Score: 5/5

Safe to merge — fix is targeted, logic is correct, and the only finding is a minor temp-file cleanup suggestion.

The core approach (temp-copy → sign → copy-back) is well-reasoned and correctly addresses the root cause. cp overwrites the destination in-place so $fw_binary retains its original permissions after copy-back. mktemp creates a file readable/writable by the owner, which is sufficient for codesign to embed a signature. The sole P2 finding (no trap cleanup on failure) is inconsequential in CI and does not affect correctness.

No files require special attention.

Vulnerabilities

No security concerns identified. The temp file is created with mktemp (mode 0600, unguessable name) and removed immediately after use; no secrets or credentials are written to it.

Important Files Changed

Filename Overview
scripts/package/build_app_tauri.sh Adds temp-copy workaround for signing a non-standard PyInstaller-embedded Python.framework binary; logic is sound and permissions are preserved correctly on copy-back.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Step 2: iterate .framework / .bundle / .plugin dirs] --> B["codesign bundle in-place"]
    B --> C{Success?}
    C -- Yes --> D[Log 'Signed bundle']
    C -- No --> E{Error: 'bundle format is ambiguous'?}
    E -- No --> F[Exit with error]
    E -- Yes --> G["Derive main binary path\n(e.g. Python.framework/Python)"]
    G --> H{Binary exists?}
    H -- No --> I[Exit with error]
    H -- Yes --> J["mktemp → tmp_binary"]
    J --> K["cp fw_binary → tmp_binary"]
    K --> L["codesign tmp_binary\n(no .framework context)"]
    L --> M["cp tmp_binary → fw_binary\n(signed, permissions preserved)"]
    M --> N["rm tmp_binary"]
    N --> O[Continue to next bundle]
    D --> O
Loading

Reviews (1): Last reviewed commit: "fix(build): sign non-standard framework ..." | Re-trigger Greptile

Comment on lines +165 to +169
tmp_binary=$(mktemp)
cp "$fw_binary" "$tmp_binary"
sign_binary "$tmp_binary"
cp "$tmp_binary" "$fw_binary"
rm -f "$tmp_binary"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Temp file not cleaned up on signing failure

If sign_binary "$tmp_binary" fails (non-zero exit), set -e aborts the script before rm -f "$tmp_binary" runs, leaving the temp file in /tmp. In CI this is harmless since the machine isn't reused, but a trap guard would make this leak-proof in any environment.

Suggested change
tmp_binary=$(mktemp)
cp "$fw_binary" "$tmp_binary"
sign_binary "$tmp_binary"
cp "$tmp_binary" "$fw_binary"
rm -f "$tmp_binary"
tmp_binary=$(mktemp)
trap 'rm -f "$tmp_binary"' EXIT
cp "$fw_binary" "$tmp_binary"
sign_binary "$tmp_binary"
cp "$tmp_binary" "$fw_binary"
rm -f "$tmp_binary"
trap - EXIT

@ErikBjare ErikBjare merged commit 93053d0 into ActivityWatch:master Apr 9, 2026
17 checks passed
TimeToBuildBob added a commit to TimeToBuildBob/activitywatch that referenced this pull request Apr 9, 2026
… ambiguity (ActivityWatch#1250)

codesign refuses to sign Python.framework/Python in-place when the binary
is inside a .framework directory — it sees the directory context and reports
'bundle format is ambiguous (could be app or framework)'.

The ActivityWatch#1249 fallback correctly detected this case but then called sign_binary
on the same path, which hits the same codesign check.

Fix: copy the binary to a temp path outside any .framework dir, sign it
there, then copy the signed binary back. Code signatures are embedded in
the Mach-O binary (not path-dependent), so the result is identical.

This should be the final fix needed to unblock the Build Tauri master CI
and allow the Thursday 2026-04-09 12:00 UTC scheduled dev release to run.
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