fix(build): handle ambiguous bundle format in codesign Step 2#1248
fix(build): handle ambiguous bundle format in codesign Step 2#1248TimeToBuildBob wants to merge 3 commits intoActivityWatch:masterfrom
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.
Python.framework (created by PyInstaller) has an ambiguous bundle structure that causes codesign to error with "bundle format is ambiguous (could be app or framework)" when signed as a bundle. The previous fix (ActivityWatch#1247) only skipped the framework's main binary in Step 1 (per-binary signing). But Step 2 (bundle-level signing) also encounters this error when trying to sign the framework bundle itself. Fix: make bundle signing non-fatal in Step 2. Since all individual Mach-O files inside the framework are already signed in Step 1, and the top-level .app is signed in Step 3, the framework-level seal is not strictly required.
Greptile SummaryThis PR makes Step 2 of the inside-out macOS codesign process non-fatal so that
Confidence Score: 4/5Safe to merge for the targeted CI fix, but the overly broad error suppression in Step 2 should be addressed to avoid masking real signing failures in future. One P1 finding: all Step 2 codesign failures are silently swallowed, not just the known Python.framework ambiguity. This can produce an app with unsigned nested bundles that fails notarization without a clear error. A P2 log-clarity issue also exists but does not affect correctness. scripts/package/build_app_tauri.sh — Step 2 error handling (lines 149–155)
|
| Filename | Overview |
|---|---|
| scripts/package/build_app_tauri.sh | Step 2 now tolerates all codesign failures non-fatally, not just the intended Python.framework ambiguity — legitimate bundle signing errors will be silently ignored, risking unsigned nested bundles that cause notarization failures. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Start signing\nAPPLE_PERSONALID set] --> B
B["Step 1: Sign all Mach-O leaf files\n(dylibs, .so, executables)\nSort by path depth descending"]
B --> C{Parent dir is\n.framework/.bundle/.plugin?}
C -- yes --> D[Skip — signed\nas bundle in Step 2]
C -- no --> E[codesign --force\n--options runtime\n--timestamp]
E --> F
F["Step 2: Sign bundle dirs\n(.framework, .bundle, .plugin)\nDeepest first"]
F --> G{sign_binary\nsucceeds?}
G -- yes --> H[Bundle signed ✓]
G -- no --> I["⚠ WARNING printed\nscript continues\n(ALL failures, not just ambiguity)"]
H --> J
I --> J
J["Step 3: Sign top-level\ndist/ActivityWatch.app"]
J --> K[Signing complete ✓]
style I fill:#ffcccc,stroke:#cc0000
Comments Outside Diff (1)
-
scripts/package/build_app_tauri.sh, line 107-113 (link)sign_binaryprints "Signing:" before a potential skipWhen Step 2 calls
sign_binary "$fw"and it fails, the console/CI log showsSigning: Python.frameworkimmediately followed by the codesign error and then the WARNING. This looks like signing was attempted and partially succeeded, which can be misleading during log triage. Consider moving theechooutsidesign_binaryor into the caller so you can choose the right message based on outcome.
Reviews (1): Last reviewed commit: "fix(build): handle ambiguous bundle form..." | Re-trigger Greptile
| if ! sign_binary "$fw"; then | ||
| echo " WARNING: Skipping $fw (bundle format may be ambiguous, contents already signed in Step 1)" |
There was a problem hiding this comment.
All Step 2 failures silently swallowed, not just ambiguity errors
if ! sign_binary "$fw" catches every non-zero exit from codesign, not only the bundle format is ambiguous case. If a legitimately structured .bundle or .plugin fails to sign for an unrelated reason (wrong permissions, a corrupted binary, a keychain timeout), the script continues with a quietly unsigned nested bundle. The resulting .app will then fail notarization with a cryptic integrity error that is much harder to trace back to this step.
A more targeted fix captures codesign's stderr and only suppresses the known-ambiguous error, re-raising anything else:
while IFS= read -r fw; do
cs_err=$(codesign --force --options runtime --timestamp \
--entitlements "$ENTITLEMENTS" --sign "$APPLE_PERSONALID" \
"$fw" 2>&1) || {
if echo "$cs_err" >&2 && echo "$cs_err" | grep -q "bundle format is ambiguous"; then
echo " WARNING: Skipping $fw (bundle format is ambiguous, contents already signed in Step 1)"
else
echo " ERROR: codesign failed for $fw" >&2
echo "$cs_err" >&2
exit 1
fi
}
done < <(...)This keeps the fix surgical — only the known PyInstaller ambiguity is tolerated; every other signing failure still aborts the build.
… only The previous fix suppressed all Step 2 codesign failures, which could silently produce an app with unsigned nested bundles (failing notarization without a clear error). Now only the known Python.framework ambiguity error is swallowed; any other signing failure aborts the build immediately with an error message. Addresses Greptile P1 review finding on PR ActivityWatch#1248.
|
Superseded by #1249, which handles the case more correctly: when Step 2 finds an ambiguous-format bundle (e.g. PyInstaller's Python.framework), it falls back to explicitly signing the main binary inside, rather than just warning and skipping. This is necessary because the main binary (e.g. Python.framework/Python) is also skipped in Step 1 — so the skip-in-both-steps approach in this PR could leave it unsigned. |
Summary
Python.framework: bundle format is ambiguous (could be app or framework)error that fails master CIContext
PR #1247 fixed the same ambiguity error in Step 1 (per-binary signing) by skipping framework main binaries. However, Step 2 (bundle-level signing) still tries to sign
Python.frameworkas a bundle, which fails because PyInstaller creates a non-standard framework structure.Since all individual Mach-O files inside the framework are already signed in Step 1, and the top-level
.appis signed in Step 3, the framework-level seal is not strictly required.Master CI failure: Build Tauri run 24157066534 — both macOS jobs fail at
Package dmgstep.Test plan
.framework/.bundle/.pluginbundles still signed normally