Skip to content

Commit 7d52fcd

Browse files
fix(build): sign canonical framework binary once and sync to duplicates (#1256)
* fix(build): sign canonical framework binary once and sync to duplicates PyInstaller copies Python.framework contents as separate regular files — Python, Versions/Current/Python, and Versions/3.9/Python have identical content but different inodes. The previous approach signed each independently via temp copy, producing 3 different signature blocks (different timestamps, random nonces). Apple's notarization service detects these as inconsistently signed and reports: 'The signature of the binary is invalid.' for all three paths, even though each individual signature is technically valid. Fix: sign only the first (canonical) binary via temp copy, then copy the signed result to all duplicate paths. All three end up with byte-identical content including the embedded signature, so Apple's hash check passes consistently across every path. * fix(build): guard cmp-s before syncing duplicates, use cp -p for temp copy - Use cp -p when copying canonical to temp file to preserve execute bits, making the behavior explicit rather than relying on destination inode mode. - Add cmp -s guard before overwriting each non-canonical path: if a binary differs from the canonical, sign it separately via temp copy instead of silently replacing it. This prevents silent corruption if a framework with genuinely distinct Mach-O files triggers the ambiguous-bundle fallback. Addresses Greptile P2 review findings on PR #1256.
1 parent 88eadfb commit 7d52fcd

1 file changed

Lines changed: 58 additions & 34 deletions

File tree

scripts/package/build_app_tauri.sh

Lines changed: 58 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -153,46 +153,70 @@ if [ -n "$APPLE_PERSONALID" ]; then
153153
--sign "$APPLE_PERSONALID" \
154154
"$fw" 2>&1) && echo " Signed bundle: $fw" || {
155155
if echo "$sign_output" | grep -q "bundle format is ambiguous"; then
156-
echo " Note: $fw lacks standard bundle structure; signing all Mach-O binaries inside via temp copy"
156+
echo " Note: $fw lacks standard bundle structure; signing canonical Mach-O binary and syncing to duplicates"
157157
# PyInstaller copies Python.framework contents as separate files rather
158158
# than symlinks — Python, Versions/Current/Python, and Versions/3.9/Python
159-
# are distinct inodes. Signing only $fw_name leaves the Versions/ copies
160-
# unsigned, causing Apple notarization to reject every affected watcher.
161-
# Sign every Mach-O file inside the framework via a temp-path copy to
162-
# avoid the in-place "bundle format is ambiguous" error from codesign.
163-
signed_count=0
159+
# are distinct inodes with identical content.
160+
#
161+
# Signing each independently produces three different signatures (different
162+
# timestamps, different random nonces in the signature block). Apple's
163+
# notarization service detects these as inconsistently signed and reports
164+
# "The signature of the binary is invalid" for all three paths.
165+
#
166+
# Fix: sign only the FIRST (canonical) binary via temp copy, then copy
167+
# the signed result to all duplicate paths. All three end up with byte-
168+
# identical content including the embedded signature, so Apple's hash
169+
# check passes for every path.
170+
fw_bins=()
164171
while IFS= read -r fw_bin; do
165-
echo " Signing framework binary via temp copy: $fw_bin"
166-
# Preserve the binary's existing code-signing identifier.
167-
# Without --identifier, codesign uses the random temp filename
168-
# (e.g. "tmp.XXXXXX") as the identifier, which makes Apple's
169-
# notarization service report "The signature of the binary is
170-
# invalid" — even though the certificate chain and code hashes
171-
# are valid. Using the original identifier (e.g. "org.python.python"
172-
# from PyInstaller's codesign_identity step) or falling back to the
173-
# binary's filename avoids this rejection.
174-
existing_id=$(codesign -d "$fw_bin" 2>&1 \
175-
| sed -n 's/^Identifier=//p' || true)
176-
if [ -z "$existing_id" ]; then
177-
existing_id=$(basename "$fw_bin")
178-
fi
179-
echo " Using identifier: $existing_id"
180-
tmp_binary=$(mktemp)
181-
cp "$fw_bin" "$tmp_binary"
182-
codesign --force --options runtime --timestamp \
183-
--entitlements "$ENTITLEMENTS" \
184-
--identifier "$existing_id" \
185-
--sign "$APPLE_PERSONALID" \
186-
"$tmp_binary" || { rm -f "$tmp_binary"; exit 1; }
187-
cp "$tmp_binary" "$fw_bin" || { rm -f "$tmp_binary"; exit 1; }
188-
rm -f "$tmp_binary"
189-
signed_count=$((signed_count + 1))
190-
done < <(find "$fw" -type f | xargs file | grep "Mach-O" | cut -d: -f1)
191-
if [ "$signed_count" -eq 0 ]; then
172+
fw_bins+=("$fw_bin")
173+
done < <(find "$fw" -type f | xargs file | grep "Mach-O" | cut -d: -f1 | sort)
174+
if [ "${#fw_bins[@]}" -eq 0 ]; then
192175
echo "ERROR: No Mach-O binaries found inside $fw" >&2
193176
exit 1
194177
fi
195-
echo " Signed $signed_count Mach-O binary/binaries inside $fw"
178+
# Sign the canonical (first) binary via temp copy to avoid the
179+
# in-place "bundle format is ambiguous" error from codesign.
180+
canonical="${fw_bins[0]}"
181+
existing_id=$(codesign -d "$canonical" 2>&1 \
182+
| sed -n 's/^Identifier=//p' || true)
183+
if [ -z "$existing_id" ]; then
184+
existing_id=$(basename "$canonical")
185+
fi
186+
echo " Signing canonical framework binary: $canonical (identifier: $existing_id)"
187+
tmp_binary=$(mktemp)
188+
cp -p "$canonical" "$tmp_binary"
189+
codesign --force --options runtime --timestamp \
190+
--entitlements "$ENTITLEMENTS" \
191+
--identifier "$existing_id" \
192+
--sign "$APPLE_PERSONALID" \
193+
"$tmp_binary" || { rm -f "$tmp_binary"; exit 1; }
194+
cp "$tmp_binary" "$canonical" || { rm -f "$tmp_binary"; exit 1; }
195+
rm -f "$tmp_binary"
196+
# Copy the signed canonical to all duplicate paths so they share
197+
# byte-identical signatures (Apple notarization checks all paths).
198+
# Guard with cmp -s so genuinely distinct binaries are signed
199+
# separately rather than silently overwritten.
200+
for fw_bin in "${fw_bins[@]:1}"; do
201+
if cmp -s "$canonical" "$fw_bin"; then
202+
echo " Syncing signed binary to duplicate path: $fw_bin"
203+
cp "$canonical" "$fw_bin" || exit 1
204+
else
205+
echo " WARNING: $fw_bin differs from canonical; signing separately" >&2
206+
tmp2=$(mktemp)
207+
fw_id=$(codesign -d "$fw_bin" 2>&1 | sed -n 's/^Identifier=//p' || true)
208+
[ -z "$fw_id" ] && fw_id=$(basename "$fw_bin")
209+
cp -p "$fw_bin" "$tmp2"
210+
codesign --force --options runtime --timestamp \
211+
--entitlements "$ENTITLEMENTS" \
212+
--identifier "$fw_id" \
213+
--sign "$APPLE_PERSONALID" \
214+
"$tmp2" || { rm -f "$tmp2"; exit 1; }
215+
cp "$tmp2" "$fw_bin" || { rm -f "$tmp2"; exit 1; }
216+
rm -f "$tmp2"
217+
fi
218+
done
219+
echo " Signed 1 + synced $((${#fw_bins[@]} - 1)) duplicate(s) inside $fw"
196220
else
197221
echo "ERROR: Failed to sign $fw: $sign_output" >&2
198222
exit 1

0 commit comments

Comments
 (0)