Files
feedBack-desktop/scripts/sign-macos-binaries.sh
T
OmikronApexandClaude Fable 5 83b8b66fe7 fix(ci): rename entitlements file — vpk requires .entitlements extension
Nightly run 29093119570 failed:
  --signEntitlements does not have an .entitlements extension
vpk validates the file extension. Rename
resources/entitlements.mac.plist -> entitlements.mac.entitlements
and update all references (electron-builder config, sign-macos-binaries.sh,
both workflows). Content unchanged; codesign/electron-builder accept any
extension.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 15:04:31 +02:00

99 lines
3.6 KiB
Bash
Executable File

#!/bin/bash
# Sign every native binary inside resources/bin and resources/python/runtime
# with the Developer ID Application certificate so the .app passes
# notarization. Runs as part of the macOS bundle step, after binaries
# are downloaded/copied but before verify_bundled_binaries runs them
# (signing also clears the quarantine attribute that GitHub release
# downloads carry by default).
#
# Skips silently when APPLE_SIGNING_IDENTITY is unset — local builds
# without a Developer ID cert still produce a (Gatekeeper-rejected)
# unsigned .app, same as before this script existed.
#
# Required env (set by CI):
# APPLE_SIGNING_IDENTITY Full identity string, e.g.
# "Developer ID Application: Name (TEAMID)"
# Must already be present in the active keychain.
set -euo pipefail
if [[ -z "${APPLE_SIGNING_IDENTITY:-}" ]]; then
echo "[sign-macos] APPLE_SIGNING_IDENTITY not set — skipping (unsigned build)"
exit 0
fi
if [[ "$OSTYPE" != "darwin"* ]]; then
echo "[sign-macos] not on macOS — skipping" >&2
exit 0
fi
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
ENTITLEMENTS="$PROJECT_DIR/resources/entitlements.mac.entitlements"
if [[ ! -f "$ENTITLEMENTS" ]]; then
echo "[sign-macos] entitlements file missing: $ENTITLEMENTS" >&2
exit 1
fi
sign_one() {
local target="$1"
[[ -e "$target" ]] || return 0
# Skip symlinks — they get followed during bundling and signing the
# link target separately is enough.
[[ -L "$target" ]] && return 0
echo " $target"
codesign --force --options runtime --timestamp \
--sign "$APPLE_SIGNING_IDENTITY" \
--entitlements "$ENTITLEMENTS" \
"$target"
}
echo "[sign-macos] signing bundled binaries with: $APPLE_SIGNING_IDENTITY"
# 1. Top-level executables and bundled dylibs in resources/bin.
# dylibbundler copies fluidsynth's deps into this directory and
# rewrites their install names to @executable_path/, so each one
# has to be signed individually before the app bundle is built.
BIN_DIR="$PROJECT_DIR/resources/bin"
if [[ -d "$BIN_DIR" ]]; then
while IFS= read -r -d '' f; do
sign_one "$f"
done < <(find "$BIN_DIR" -maxdepth 1 -type f \( -perm -u+x -o -name '*.dylib' \) -print0)
fi
# 2. Embedded CPython runtime — interpreter + libpython dylib + every
# compiled extension. Notarization rejects the bundle if any of
# these is unsigned.
PY_RUNTIME="$PROJECT_DIR/resources/python/runtime"
if [[ -d "$PY_RUNTIME" ]]; then
# Interpreter binaries (python3, python3.x, etc.)
while IFS= read -r -d '' f; do
sign_one "$f"
done < <(find "$PY_RUNTIME/bin" -maxdepth 1 -type f -perm -u+x -print0 2>/dev/null || true)
# libpython dylib(s) and any other shared libs
while IFS= read -r -d '' f; do
sign_one "$f"
done < <(find "$PY_RUNTIME/lib" -maxdepth 2 -type f -name '*.dylib' -print0 2>/dev/null || true)
# Compiled extension modules — both stdlib lib-dynload and any
# site-packages C extensions installed via pip.
while IFS= read -r -d '' f; do
sign_one "$f"
done < <(find "$PY_RUNTIME/lib" -type f \( -name '*.so' -o -name '*.dylib' \) -print0 2>/dev/null || true)
fi
echo "[sign-macos] done"
# Spot-check a couple of binaries so a botched sign call fails the
# build here rather than mid-notarization 2 minutes later.
for probe in "$BIN_DIR/fluidsynth" "$BIN_DIR/ffmpeg" "$BIN_DIR/vgmstream-cli"; do
if [[ -f "$probe" ]]; then
codesign --verify --strict "$probe" || {
echo "[sign-macos] verification failed: $probe" >&2
exit 1
}
fi
done