Ariver
2026-06-24 e533a8cd34995af9aaeeb321b41969a260fef882
privatevoice.src/scripts/build-macappstore-pkg-macos.sh
@@ -20,6 +20,7 @@
# Optional environment:
#   PRIVATEVOICE_SIGNING_KEYCHAIN
#   PRIVATEVOICE_APPSTORE_MIN_SYSTEM_VERSION (default: 13.4)
#   PRIVATEVOICE_MACAPPSTORE_BUILD_ROOT (default: build/macappstore)
#
# Usage:
#   cd privatevoice.src
@@ -39,9 +40,10 @@
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
MACAPPSTORE_BUILD_ROOT="${PRIVATEVOICE_MACAPPSTORE_BUILD_ROOT:-$PROJECT_DIR/build/macappstore}"
INFO_PLIST="$PROJECT_DIR/build/darwin/Info.plist"
ENTITLEMENTS="$PROJECT_DIR/build/darwin/entitlements-appstore.plist"
BASE_ENTITLEMENTS="$PROJECT_DIR/build/darwin/entitlements-appstore.plist"
GOMODCACHE="$(go env GOMODCACHE)"
SHERPA_MACOS_MODULE_VERSION="$(cd "$PROJECT_DIR" && go list -m -f '{{.Version}}' github.com/k2-fsa/sherpa-onnx-go-macos)"
@@ -94,7 +96,7 @@
      ;;
  esac
  BUILD_DIR="$PROJECT_DIR/build/macappstore/$OUT_ARCH"
  BUILD_DIR="$MACAPPSTORE_BUILD_ROOT/$OUT_ARCH"
  APP_BUNDLE="$BUILD_DIR/$APP_NAME.app"
  PKG_PATH="$BUILD_DIR/PrivateVoice-Dictation-$VERSION-build$BUILD_ID-$OUT_ARCH-macappstore.pkg"
  SHERPA_LIB_DIR="$GOMODCACHE/github.com/k2-fsa/sherpa-onnx-go-macos@$SHERPA_MACOS_MODULE_VERSION/lib/$SHERPA_ARCH"
@@ -116,9 +118,60 @@
  codesign --force \
    --options runtime \
    --timestamp \
    --entitlements "$ENTITLEMENTS" \
    --entitlements "$SIGNING_ENTITLEMENTS" \
    --sign "$APPSTORE_APP_IDENTITY" \
    "$item"
}
plist_set_string() {
  local plist="$1"
  local key="$2"
  local value="$3"
  /usr/libexec/PlistBuddy -c "Delete :$key" "$plist" >/dev/null 2>&1 || true
  /usr/libexec/PlistBuddy -c "Add :$key string $value" "$plist"
}
strip_extended_attributes() {
  local item="$1"
  if command -v xattr >/dev/null; then
    chmod -R u+rwX "$item"
    xattr -cr "$item"
  fi
}
assert_no_quarantine_attributes() {
  local item="$1"
  if command -v xattr >/dev/null; then
    local found
    found="$(xattr -lr "$item" 2>/dev/null | grep -F "com.apple.quarantine" | head -n 20 || true)"
    [[ -z "$found" ]] || fail "com.apple.quarantine attributes remain under $item:
$found"
  fi
}
remove_appledouble_files() {
  local item="$1"
  find "$item" \( -name '._*' -o -name '.DS_Store' \) -delete
}
assert_no_appledouble_files() {
  local item="$1"
  local found
  found="$(find "$item" \( -name '._*' -o -name '.DS_Store' \) -print | head -n 20 || true)"
  [[ -z "$found" ]] || fail "AppleDouble or Finder metadata files remain under $item:
$found"
}
assert_pkg_no_quarantine_attributes() {
  local pkg="$1"
  local expanded
  local found
  expanded="$(mktemp -d)"
  pkgutil --expand-full "$pkg" "$expanded/pkg" >/dev/null
  found="$(xattr -lr "$expanded/pkg" 2>/dev/null | grep -F "com.apple.quarantine" | head -n 20 || true)"
  rm -rf "$expanded"
  [[ -z "$found" ]] || fail "com.apple.quarantine attributes remain in package:
$found"
}
TARGET_ARCH="${1:-$(uname -m)}"
@@ -131,7 +184,7 @@
[[ -f "$APPSTORE_PROVISIONING_PROFILE" ]] || fail "Provisioning profile not found: $APPSTORE_PROVISIONING_PROFILE"
[[ -f "$INFO_PLIST" ]] || fail "Info.plist not found at $INFO_PLIST"
[[ -n "$BUNDLE_ID" ]] || fail "CFBundleIdentifier is missing from $INFO_PLIST"
[[ -f "$ENTITLEMENTS" ]] || fail "App Store entitlements not found at $ENTITLEMENTS"
[[ -f "$BASE_ENTITLEMENTS" ]] || fail "App Store entitlements not found at $BASE_ENTITLEMENTS"
[[ -f "$PROJECT_DIR/build/darwin/icon.icns" ]] || fail "icon.icns not found"
[[ -d "$SHERPA_LIB_DIR" ]] || fail "sherpa-onnx dylibs not found at $SHERPA_LIB_DIR"
ONNXRUNTIME_DYLIB="$(find "$SHERPA_LIB_DIR" -maxdepth 1 -type f -name 'libonnxruntime.*.dylib' | sort | tail -n 1)"
@@ -145,13 +198,26 @@
command -v pkgutil >/dev/null || fail "pkgutil not found"
PROFILE_PLIST="$(mktemp)"
trap 'rm -f "$PROFILE_PLIST"' EXIT
SIGNING_ENTITLEMENTS="$(mktemp)"
trap 'rm -f "$PROFILE_PLIST" "$SIGNING_ENTITLEMENTS"' EXIT
security cms -D -i "$APPSTORE_PROVISIONING_PROFILE" >"$PROFILE_PLIST" ||
  fail "Unable to decode provisioning profile: $APPSTORE_PROVISIONING_PROFILE"
PROFILE_APP_IDENTIFIER="$(/usr/libexec/PlistBuddy -c "Print :Entitlements:com.apple.application-identifier" "$PROFILE_PLIST" 2>/dev/null || true)"
PROFILE_TEAM_IDENTIFIER="$(/usr/libexec/PlistBuddy -c "Print :Entitlements:com.apple.developer.team-identifier" "$PROFILE_PLIST" 2>/dev/null || true)"
PROFILE_KEYCHAIN_GROUP="$(/usr/libexec/PlistBuddy -c "Print :Entitlements:keychain-access-groups:0" "$PROFILE_PLIST" 2>/dev/null || true)"
PROFILE_BUNDLE_ID="${PROFILE_APP_IDENTIFIER#*.}"
[[ "$PROFILE_BUNDLE_ID" == "$BUNDLE_ID" ]] ||
  fail "Provisioning profile bundle ID mismatch: profile has $PROFILE_BUNDLE_ID, app has $BUNDLE_ID"
[[ -n "$PROFILE_TEAM_IDENTIFIER" ]] || fail "Provisioning profile missing com.apple.developer.team-identifier entitlement"
cp "$BASE_ENTITLEMENTS" "$SIGNING_ENTITLEMENTS"
plist_set_string "$SIGNING_ENTITLEMENTS" "com.apple.application-identifier" "$PROFILE_APP_IDENTIFIER"
plist_set_string "$SIGNING_ENTITLEMENTS" "com.apple.developer.team-identifier" "$PROFILE_TEAM_IDENTIFIER"
if [[ -n "$PROFILE_KEYCHAIN_GROUP" ]]; then
  /usr/libexec/PlistBuddy -c "Delete :keychain-access-groups" "$SIGNING_ENTITLEMENTS" >/dev/null 2>&1 || true
  /usr/libexec/PlistBuddy -c "Add :keychain-access-groups array" "$SIGNING_ENTITLEMENTS"
  /usr/libexec/PlistBuddy -c "Add :keychain-access-groups:0 string $PROFILE_KEYCHAIN_GROUP" "$SIGNING_ENTITLEMENTS"
fi
step "Cleaning old Mac App Store build"
rm -rf "$BUILD_DIR"
@@ -189,6 +255,12 @@
cp "$SHERPA_LIB_DIR/libsherpa-onnx-c-api.dylib" "$APP_BUNDLE/Contents/Frameworks/"
cp "$ONNXRUNTIME_DYLIB" "$APP_BUNDLE/Contents/Frameworks/"
step "Removing extended attributes from app bundle"
strip_extended_attributes "$APP_BUNDLE"
remove_appledouble_files "$APP_BUNDLE"
assert_no_quarantine_attributes "$APP_BUNDLE"
assert_no_appledouble_files "$APP_BUNDLE"
step "Fixing rpaths"
install_name_tool -add_rpath @executable_path/../Frameworks \
  "$APP_BUNDLE/Contents/MacOS/$APP_NAME" 2>/dev/null || true
@@ -203,18 +275,31 @@
sign_sandboxed "$APP_BUNDLE/Contents/MacOS/$APP_NAME"
sign_sandboxed "$APP_BUNDLE"
step "Removing extended attributes after signing"
strip_extended_attributes "$APP_BUNDLE"
remove_appledouble_files "$APP_BUNDLE"
assert_no_quarantine_attributes "$APP_BUNDLE"
assert_no_appledouble_files "$APP_BUNDLE"
step "Verifying app signature and entitlements"
codesign -vvv --deep --strict "$APP_BUNDLE"
codesign -d --entitlements :- "$APP_BUNDLE"
SIGNED_ENTITLEMENTS="$(mktemp)"
codesign -d --entitlements :- "$APP_BUNDLE" >"$SIGNED_ENTITLEMENTS" 2>/dev/null
SIGNED_APP_IDENTIFIER="$(/usr/libexec/PlistBuddy -c "Print :com.apple.application-identifier" "$SIGNED_ENTITLEMENTS" 2>/dev/null || true)"
rm -f "$SIGNED_ENTITLEMENTS"
[[ "$SIGNED_APP_IDENTIFIER" == "$PROFILE_APP_IDENTIFIER" ]] ||
  fail "Signed app application-identifier mismatch: signed has $SIGNED_APP_IDENTIFIER, profile has $PROFILE_APP_IDENTIFIER"
step "Creating signed installer package"
productbuild \
COPYFILE_DISABLE=1 productbuild \
  --sign "$APPSTORE_INSTALLER_IDENTITY" \
  --component "$APP_BUNDLE" /Applications \
  "$PKG_PATH"
step "Verifying installer package signature"
pkgutil --check-signature "$PKG_PATH"
assert_pkg_no_quarantine_attributes "$PKG_PATH"
step "Mac App Store package complete"
echo "  App: $APP_BUNDLE"