Ariver
2026-07-11 015f1089ea875a153dd2a18ef42c25145d4868f5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#!/usr/bin/env bash
#
# build-local-macos-universal.sh - Build a locally signed universal macOS DMG.
#
# The script reuses the existing per-architecture local build script, then
# combines the app binary and bundled dylibs with lipo before re-signing.
#
set -euo pipefail
 
APP_NAME="PrivateVoice Dictation"
DEFAULT_LOCAL_IDENTITY="PrivateVoice Local Code Signing"
LEGACY_LOCAL_IDENTITY="VoiceSnap Local Code Signing"
LOCAL_IDENTITY="${PRIVATEVOICE_LOCAL_SIGN_IDENTITY:-${VOICESNAP_LOCAL_SIGN_IDENTITY:-}}"
KEYCHAIN="${PRIVATEVOICE_LOCAL_KEYCHAIN:-${VOICESNAP_LOCAL_KEYCHAIN:-$HOME/Library/Keychains/login.keychain-db}}"
LOCAL_BUILD_FLAVOR="${PRIVATEVOICE_LOCAL_BUILD_FLAVOR:-local}"
 
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
 
INFO_PLIST="$PROJECT_DIR/build/darwin/Info.plist"
ENTITLEMENTS="$PROJECT_DIR/build/darwin/entitlements.plist"
VERSION="$(defaults read "$INFO_PLIST" CFBundleShortVersionString 2>/dev/null || echo "2.1.1")"
BUILD_ID="$(defaults read "$INFO_PLIST" CFBundleVersion 2>/dev/null || date +%Y%m%d.%H%M)"
 
ARM_BUILD_DIR="$PROJECT_DIR/build/local/arm64"
X86_BUILD_DIR="$PROJECT_DIR/build/local/x86_64"
BUILD_DIR="$PROJECT_DIR/build/local/universal"
ARM_APP="$ARM_BUILD_DIR/$APP_NAME.app"
X86_APP="$X86_BUILD_DIR/$APP_NAME.app"
APP_BUNDLE="$BUILD_DIR/$APP_NAME.app"
DMG_PATH="$BUILD_DIR/$APP_NAME-$VERSION-build$BUILD_ID-universal-$LOCAL_BUILD_FLAVOR.dmg"
COMPAT_DMG_PATH="$BUILD_DIR/$APP_NAME-$VERSION-universal-$LOCAL_BUILD_FLAVOR.dmg"
 
step() {
  echo ""
  echo "==> $1"
}
 
fail() {
  echo "ERROR: $1" >&2
  exit 1
}
 
resolve_local_identity() {
  if [[ -z "$LOCAL_IDENTITY" ]]; then
    if security find-identity -v -p codesigning "$KEYCHAIN" | grep -Fq "$DEFAULT_LOCAL_IDENTITY"; then
      LOCAL_IDENTITY="$DEFAULT_LOCAL_IDENTITY"
    elif security find-identity -v -p codesigning "$KEYCHAIN" | grep -Fq "$LEGACY_LOCAL_IDENTITY"; then
      LOCAL_IDENTITY="$LEGACY_LOCAL_IDENTITY"
    else
      fail "Local signing identity not found; run build-local-macos.sh first"
    fi
  fi
 
  if ! security find-identity -v -p codesigning "$KEYCHAIN" | grep -Fq "$LOCAL_IDENTITY"; then
    fail "Local signing identity not available to codesign: $LOCAL_IDENTITY"
  fi
}
 
sign_item() {
  local item="$1"
  codesign --force \
    --options runtime \
    --timestamp=none \
    --entitlements "$ENTITLEMENTS" \
    --sign "$LOCAL_IDENTITY" \
    "$item"
}
 
step "Building arm64 app"
"$SCRIPT_DIR/build-local-macos.sh" arm64
 
step "Building x86_64 app"
"$SCRIPT_DIR/build-local-macos.sh" x86_64
 
step "Preflight checks"
[[ -d "$ARM_APP" ]] || fail "arm64 app not found at $ARM_APP"
[[ -d "$X86_APP" ]] || fail "x86_64 app not found at $X86_APP"
[[ -f "$ENTITLEMENTS" ]] || fail "entitlements.plist not found at $ENTITLEMENTS"
resolve_local_identity
 
step "Assembling universal app"
rm -rf "$BUILD_DIR"
mkdir -p "$BUILD_DIR"
ditto "$ARM_APP" "$APP_BUNDLE"
 
lipo -create \
  "$ARM_APP/Contents/MacOS/$APP_NAME" \
  "$X86_APP/Contents/MacOS/$APP_NAME" \
  -output "$APP_BUNDLE/Contents/MacOS/$APP_NAME.universal"
mv "$APP_BUNDLE/Contents/MacOS/$APP_NAME.universal" "$APP_BUNDLE/Contents/MacOS/$APP_NAME"
 
for arm_dylib in "$ARM_APP"/Contents/Frameworks/*.dylib; do
  dylib_name="$(basename "$arm_dylib")"
  x86_dylib="$X86_APP/Contents/Frameworks/$dylib_name"
  out_dylib="$APP_BUNDLE/Contents/Frameworks/$dylib_name"
  out_tmp="$out_dylib.universal"
  [[ -f "$x86_dylib" ]] || fail "Matching x86_64 dylib not found: $x86_dylib"
  lipo -create "$arm_dylib" "$x86_dylib" -output "$out_tmp"
  mv "$out_tmp" "$out_dylib"
done
 
step "Code signing universal app"
for dylib in "$APP_BUNDLE"/Contents/Frameworks/*.dylib; do
  sign_item "$dylib"
done
sign_item "$APP_BUNDLE/Contents/MacOS/$APP_NAME"
sign_item "$APP_BUNDLE"
 
step "Verifying universal app"
codesign -vvv --deep --strict "$APP_BUNDLE"
lipo -archs "$APP_BUNDLE/Contents/MacOS/$APP_NAME"
 
step "Creating universal DMG"
DMG_TEMP="$BUILD_DIR/dmg-staging"
rm -rf "$DMG_TEMP" "$DMG_PATH" "$COMPAT_DMG_PATH"
mkdir -p "$DMG_TEMP"
cp -R "$APP_BUNDLE" "$DMG_TEMP/"
ln -s /Applications "$DMG_TEMP/Applications"
hdiutil create -volname "$APP_NAME" \
  -srcfolder "$DMG_TEMP" \
  -ov -format UDZO \
  "$DMG_PATH"
rm -rf "$DMG_TEMP"
 
step "Signing universal DMG"
codesign --force --timestamp=none --sign "$LOCAL_IDENTITY" "$DMG_PATH"
hdiutil verify "$DMG_PATH"
cp "$DMG_PATH" "$COMPAT_DMG_PATH"
 
step "Universal local build complete"
echo "  App: $APP_BUNDLE"
echo "  DMG: $DMG_PATH"
echo "  Compatibility DMG: $COMPAT_DMG_PATH"
echo "  Signing identity: $LOCAL_IDENTITY"