Skip to content

Commit e60719b

Browse files
authored
fix(build): restore Python.framework symlinks before signing (#1260)
* fix(build): restore Python.framework symlinks before signing PyInstaller copies Python.framework using real files/directories instead of preserving the standard macOS symlink layout. This causes codesign to reject the framework with "bundle format is ambiguous", triggering a fallback path that signs individual binaries without creating a proper framework bundle signature. Apple's notarization then rejects all Python.framework binaries with "The signature of the binary is invalid." Fix: after copying watchers into the .app bundle, restore the canonical macOS framework layout using symlinks: - Versions/Current -> <version> - Python -> Versions/Current/Python - Resources -> Versions/Current/Resources This lets codesign sign the framework as a proper bundle (verified locally), producing valid bundle signatures that Apple should accept. * fix: address Greptile review - use glob for version discovery, simplify find - Replace ls|grep|head with glob loop for version dir discovery (avoids SIGPIPE and locale issues) - Use find -iname "Python.framework" instead of -name "*.framework"|grep
1 parent c9c256b commit e60719b

1 file changed

Lines changed: 40 additions & 0 deletions

File tree

scripts/package/build_app_tauri.sh

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,46 @@ for component in dist/activitywatch/*/; do
4545
fi
4646
done
4747

48+
echo "Fixing Python.framework symlink structure..."
49+
# PyInstaller copies Python.framework using regular files/directories instead of
50+
# preserving the standard macOS symlink layout. Without symlinks, codesign rejects
51+
# the framework with "bundle format is ambiguous (could be app or framework)".
52+
# Restore the canonical layout so codesign can sign it as a proper framework bundle:
53+
# Versions/Current -> <version> (symlink)
54+
# Python -> Versions/Current/Python (symlink)
55+
# Resources -> Versions/Current/Resources (symlink)
56+
# Headers -> Versions/Current/Headers (symlink, if present)
57+
while IFS= read -r fw; do
58+
echo " Fixing: $fw"
59+
# Find the actual version directory (e.g., "3.9"), skipping "Current"
60+
version_dir=""
61+
for d in "$fw/Versions"/*/; do
62+
bname="$(basename "$d")"
63+
if [ "$bname" != "Current" ] && [ -d "$d" ]; then
64+
version_dir="$bname"
65+
break
66+
fi
67+
done
68+
if [ -z "$version_dir" ]; then
69+
echo " Warning: No version directory found in $fw/Versions/, skipping"
70+
continue
71+
fi
72+
73+
# Replace Versions/Current directory with a symlink to the version dir
74+
if [ -d "$fw/Versions/Current" ] && [ ! -L "$fw/Versions/Current" ]; then
75+
rm -rf "$fw/Versions/Current"
76+
ln -s "$version_dir" "$fw/Versions/Current"
77+
fi
78+
79+
# Replace root-level copies with symlinks into Versions/Current/
80+
for item in Python Resources Headers; do
81+
if [ -e "$fw/$item" ] && [ ! -L "$fw/$item" ]; then
82+
rm -rf "$fw/$item"
83+
ln -s "Versions/Current/$item" "$fw/$item"
84+
fi
85+
done
86+
done < <(find "dist/${APP_NAME}.app" -type d -iname "Python.framework")
87+
4888
echo "Setting executable permissions..."
4989
find "dist/${APP_NAME}.app/Contents/Resources" -type f -name "aw-*" -exec chmod +x {} \;
5090

0 commit comments

Comments
 (0)