fix(build): handle non-standard Python.framework signing in Tauri build#1249
Conversation
…ambiguity error The inside-out signing loop from ActivityWatch#1246 signs ALL Mach-O files in Step 1, then .framework bundles in Step 2. But Python.framework/Python is both a Mach-O binary AND the main binary of a .framework bundle — codesign errors with "bundle format is ambiguous (could be app or framework)" when it's signed as a standalone file. Fix: Skip files whose parent directory is a .framework, .bundle, or .plugin in Step 1. These are correctly signed as part of their bundle in Step 2. Also extends Step 2 to cover .bundle and .plugin directories (not just .framework) for completeness. Fixes the Build Tauri master CI failure after ActivityWatch#1246 merge.
Greptile SummaryThis PR fixes a post-merge CI failure in the Tauri macOS build by adding a fallback in the Step 2 codesign loop: when Confidence Score: 5/5Safe to merge — the fix is targeted, non-standard framework signing errors are caught precisely, and all other codesign failures remain fatal. The only finding is a P2 observability suggestion (silent skip warning instead of hard exit when the fallback binary is absent). This does not affect correctness for the known PyInstaller layout, and the existing No files require special attention.
|
| Filename | Overview |
|---|---|
| scripts/package/build_app_tauri.sh | Adds fallback in Step 2 codesign loop: catches "bundle format is ambiguous" for non-standard PyInstaller-embedded frameworks and signs the main binary directly; all other codesign errors remain fatal. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Step 1: Sign all Mach-O files\nlength-sorted deepest-first] --> B{Parent dir is\n.framework/.bundle/.plugin?}
B -- Yes --> C[Skip — will be handled\nin Step 2]
B -- No --> D[sign_binary file]
C --> E
D --> E[Step 2: Sign bundle directories\n.framework / .bundle / .plugin\nlength-sorted deepest-first]
E --> F{codesign succeeds?}
F -- Yes --> G[Signed bundle ✓]
F -- No --> H{Error contains\n'bundle format\nis ambiguous'?}
H -- Yes --> I[Fallback: sign main binary\ne.g. Python.framework/Python]
I --> J{Binary exists\nat fw/fw_name?}
J -- Yes --> K[sign_binary fw_binary ✓]
J -- No --> L[Warning: skip —\nbinary not found]
H -- No --> M[echo ERROR + exit 1 ✗]
G --> N[Step 3: Sign top-level\n.app bundle last]
K --> N
L --> N
Reviews (1): Last reviewed commit: "fix(build): handle non-standard Python.f..." | Re-trigger Greptile
| echo " Warning: No main binary found at $fw_binary, skipping" | ||
| fi |
There was a problem hiding this comment.
Silent skip may hide unsigned components
If the main binary isn't found at $fw_binary, the framework is left unsigned with just a warning. Because notarytool's rejection message for unsigned embedded content doesn't always name the missing binary, a silent skip here could produce a confusing downstream notarization failure rather than a clear build error.
Consider making this fatal, or at least surfacing it more loudly (e.g., exit 1), since a missing main binary at the expected path likely indicates a structural change in PyInstaller's output that needs attention.
There was a problem hiding this comment.
Fixed in c2c0af4: changed the silent-skip to a fatal error (exit 1 with a clear message). If $fw_binary is not found, it now prints ERROR: Expected main binary not found at $fw_binary + a hint about PyInstaller output changes, then exits. A missing binary at this point means PyInstaller changed its structure, which warrants a loud build failure rather than a silent skip that could cause confusing notarization errors later.
PyInstaller-embedded Python.framework bundles (inside aw-watcher-window, aw-watcher-input) lack the standard Versions/ directory structure and Info.plist, so codesign rejects them in Step 2 with: bundle format is ambiguous (could be app or framework) Previously ActivityWatch#1247 correctly skipped the Python.framework/Python binary in Step 1 (standalone signing of the main framework binary causes ambiguous errors), but Step 2 still unconditionally attempted bundle-signing the .framework directory itself, hitting the same error. Fix: in Step 2, catch "bundle format is ambiguous" errors and fall back to signing the main binary inside the framework directly (e.g. Python.framework/Python). All other codesign errors remain fatal. This completes the signing chain: the .so files inside Python.framework are signed in Step 1, the Python binary itself is signed in this Step 2 fallback, and standard frameworks/bundles are signed normally.
If the main binary is not found at the expected path after an ambiguous-bundle error, emit a clear error and exit 1 rather than silently skipping. A missing binary here means PyInstaller changed its output structure — a silent skip would produce a confusing downstream notarization failure instead of a clear build error. Addresses Greptile review comment (P2) on ActivityWatch#1249.
7b1d644 to
c2c0af4
Compare
… 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.
… ambiguity (#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 #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.
…ld (ActivityWatch#1249) * fix(build): skip framework main binaries in codesign Step 1 to avoid ambiguity error The inside-out signing loop from ActivityWatch#1246 signs ALL Mach-O files in Step 1, then .framework bundles in Step 2. But Python.framework/Python is both a Mach-O binary AND the main binary of a .framework bundle — codesign errors with "bundle format is ambiguous (could be app or framework)" when it's signed as a standalone file. Fix: Skip files whose parent directory is a .framework, .bundle, or .plugin in Step 1. These are correctly signed as part of their bundle in Step 2. Also extends Step 2 to cover .bundle and .plugin directories (not just .framework) for completeness. Fixes the Build Tauri master CI failure after ActivityWatch#1246 merge. * fix(build): handle non-standard Python.framework signing in Tauri build PyInstaller-embedded Python.framework bundles (inside aw-watcher-window, aw-watcher-input) lack the standard Versions/ directory structure and Info.plist, so codesign rejects them in Step 2 with: bundle format is ambiguous (could be app or framework) Previously ActivityWatch#1247 correctly skipped the Python.framework/Python binary in Step 1 (standalone signing of the main framework binary causes ambiguous errors), but Step 2 still unconditionally attempted bundle-signing the .framework directory itself, hitting the same error. Fix: in Step 2, catch "bundle format is ambiguous" errors and fall back to signing the main binary inside the framework directly (e.g. Python.framework/Python). All other codesign errors remain fatal. This completes the signing chain: the .so files inside Python.framework are signed in Step 1, the Python binary itself is signed in this Step 2 fallback, and standard frameworks/bundles are signed normally. * fix(build): make missing fw_binary fatal instead of silent skip If the main binary is not found at the expected path after an ambiguous-bundle error, emit a clear error and exit 1 rather than silently skipping. A missing binary here means PyInstaller changed its output structure — a silent skip would produce a confusing downstream notarization failure instead of a clear build error. Addresses Greptile review comment (P2) on ActivityWatch#1249.
… 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.
Problem
Post-merge master
Build Tauriis failing on both macOS runners with:This is happening in Step 2 of the inside-out codesign loop (introduced in #1246, patched in #1247), which tries to sign all
.frameworkdirectories as bundles. PyInstaller-embeddedPython.frameworkbundles (insideaw-watcher-windowandaw-watcher-input) lack the standardVersions/directory structure andInfo.plistthatcodesignrequires to sign a proper framework bundle, so it rejects them with "bundle format is ambiguous".#1247 correctly fixed Step 1 (skipping the
Python.framework/Pythonbinary as a standalone file). But Step 2 still unconditionally calledsign_binaryon the.frameworkdirectory itself, hitting the same ambiguous-bundle error.Fix
In Step 2, catch
"bundle format is ambiguous"errors and fall back to signing the main binary inside the framework directly (e.g.Python.framework/Python). All othercodesignerrors remain fatal so real signing problems are still surfaced.This completes the signing chain:
.sofiles and sub-binaries insidePython.framework→ signed in Step 1Python.framework/Python(skipped in Step 1 by fix(build): skip framework main binaries in codesign Step 1 #1247) → signed via Step 2 fallbackRelated