| 1 |
|
| 2 |
set -euo pipefail |
| 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 |
cd "$(dirname "$0")/.." |
| 40 |
|
| 41 |
|
| 42 |
|
| 43 |
|
| 44 |
|
| 45 |
|
| 46 |
SIGNING_IDENTITY="${APPLE_SIGNING_IDENTITY:-Developer ID Application: FILL_ME (93C54W92UP)}" |
| 47 |
|
| 48 |
|
| 49 |
|
| 50 |
|
| 51 |
|
| 52 |
|
| 53 |
API_KEY_ID="${APPLE_API_KEY_ID:-}" |
| 54 |
API_ISSUER_ID="${APPLE_API_ISSUER_ID:-}" |
| 55 |
API_KEY_PATH="${APPLE_API_KEY_PATH:-}" |
| 56 |
|
| 57 |
DMG_GLOB="target/release/bundle/dmg/*.dmg" |
| 58 |
APP_GLOB="target/release/bundle/macos/GoingsOn.app" |
| 59 |
DIST_DIR="${HOME}/Dist/goingson/macos" |
| 60 |
|
| 61 |
|
| 62 |
VERIFY=true |
| 63 |
USE_KEYCHAIN=false |
| 64 |
NOTARIZE=true |
| 65 |
for arg in "$@"; do |
| 66 |
case "$arg" in |
| 67 |
--no-verify) VERIFY=false ;; |
| 68 |
--keychain) USE_KEYCHAIN=true ;; |
| 69 |
--sign-only) NOTARIZE=false ;; |
| 70 |
-h|--help) |
| 71 |
echo "Usage: $0 [--keychain] [--sign-only] [--no-verify]" |
| 72 |
exit 0 |
| 73 |
;; |
| 74 |
esac |
| 75 |
done |
| 76 |
|
| 77 |
|
| 78 |
VERSION=$(python3 -c "import json; print(json.load(open('src-tauri/tauri.conf.json'))['version'])") |
| 79 |
echo "Version: $VERSION" |
| 80 |
|
| 81 |
|
| 82 |
if [ "$USE_KEYCHAIN" = true ]; then |
| 83 |
|
| 84 |
source "$(dirname "$0")/build-keychain.sh" |
| 85 |
trap bk_teardown EXIT |
| 86 |
bk_setup |
| 87 |
SIGNING_IDENTITY="$(bk_identity_name)" |
| 88 |
if [ -z "$SIGNING_IDENTITY" ]; then |
| 89 |
echo "Error: no Developer ID Application identity in the build keychain." |
| 90 |
echo "Check BUILD_P12_PATH points at a Developer ID Application .p12." |
| 91 |
exit 1 |
| 92 |
fi |
| 93 |
echo "Build-keychain identity: $SIGNING_IDENTITY" |
| 94 |
fi |
| 95 |
|
| 96 |
|
| 97 |
if [[ "$SIGNING_IDENTITY" == *FILL_ME* ]]; then |
| 98 |
echo "Error: SIGNING_IDENTITY is still the placeholder." |
| 99 |
echo "Either pass --keychain (derives it from the .p12) or set APPLE_SIGNING_IDENTITY" |
| 100 |
echo "from: security find-identity -v -p codesigning | grep 'Developer ID Application'" |
| 101 |
exit 1 |
| 102 |
fi |
| 103 |
if [ "$NOTARIZE" = true ]; then |
| 104 |
|
| 105 |
|
| 106 |
|
| 107 |
missing="" |
| 108 |
[ -n "$API_KEY_ID" ] || missing="$missing APPLE_API_KEY_ID" |
| 109 |
[ -n "$API_ISSUER_ID" ] || missing="$missing APPLE_API_ISSUER_ID" |
| 110 |
[ -n "$API_KEY_PATH" ] || missing="$missing APPLE_API_KEY_PATH" |
| 111 |
if [ -n "$missing" ]; then |
| 112 |
echo "Error: notarization needs:$missing" |
| 113 |
echo "Source the env file first (source ~/.tauri/passwords.env), or pass --sign-only." |
| 114 |
exit 1 |
| 115 |
fi |
| 116 |
if [ ! -f "$API_KEY_PATH" ]; then |
| 117 |
echo "Error: API key not found at $API_KEY_PATH (check APPLE_API_KEY_PATH, or pass --sign-only)" |
| 118 |
exit 1 |
| 119 |
fi |
| 120 |
fi |
| 121 |
if [ "$USE_KEYCHAIN" != true ] && ! security find-identity -v -p codesigning | grep -qF "$SIGNING_IDENTITY"; then |
| 122 |
echo "Error: identity not found in keychain: $SIGNING_IDENTITY" |
| 123 |
echo "Are you in a GUI session with login.keychain unlocked? (or pass --keychain for remote signing)" |
| 124 |
exit 1 |
| 125 |
fi |
| 126 |
if [ -z "${GOINGSON_TAURI_PASSWORD:-}" ]; then |
| 127 |
echo "Note: GOINGSON_TAURI_PASSWORD unset -- source ~/.tauri/passwords.env first if you want OTA updater artifacts signed." |
| 128 |
fi |
| 129 |
|
| 130 |
|
| 131 |
|
| 132 |
|
| 133 |
|
| 134 |
|
| 135 |
echo "" |
| 136 |
if [ "$NOTARIZE" = true ]; then |
| 137 |
echo "=== Building, signing, and notarizing macOS release ===" |
| 138 |
else |
| 139 |
echo "=== Building and signing macOS release (--sign-only, no notarization) ===" |
| 140 |
fi |
| 141 |
build_env=( |
| 142 |
"APPLE_SIGNING_IDENTITY=$SIGNING_IDENTITY" |
| 143 |
"TAURI_SIGNING_PRIVATE_KEY=${TAURI_SIGNING_PRIVATE_KEY:-$HOME/.tauri/goingson.key}" |
| 144 |
"TAURI_SIGNING_PRIVATE_KEY_PASSWORD=${TAURI_SIGNING_PRIVATE_KEY_PASSWORD:-${GOINGSON_TAURI_PASSWORD:-}}" |
| 145 |
) |
| 146 |
if [ "$NOTARIZE" = true ]; then |
| 147 |
build_env+=( |
| 148 |
"APPLE_API_ISSUER=$API_ISSUER_ID" |
| 149 |
"APPLE_API_KEY=$API_KEY_ID" |
| 150 |
"APPLE_API_KEY_PATH=$API_KEY_PATH" |
| 151 |
) |
| 152 |
fi |
| 153 |
|
| 154 |
|
| 155 |
|
| 156 |
env "${build_env[@]}" cargo tauri build --bundles app dmg updater |
| 157 |
|
| 158 |
DMG_PATH=$(ls -t $DMG_GLOB 2>/dev/null | head -1 || true) |
| 159 |
if [ -z "$DMG_PATH" ]; then |
| 160 |
echo "Error: no .dmg produced under target/release/bundle/dmg/" |
| 161 |
exit 1 |
| 162 |
fi |
| 163 |
echo "Built: $DMG_PATH" |
| 164 |
|
| 165 |
|
| 166 |
|
| 167 |
|
| 168 |
if [ "$VERIFY" = true ]; then |
| 169 |
echo "" |
| 170 |
echo "=== Verifying signature ===" |
| 171 |
|
| 172 |
|
| 173 |
if [ -d "$APP_GLOB" ]; then |
| 174 |
echo "--- codesign (app) ---" |
| 175 |
codesign --verify --deep --strict --verbose=2 "$APP_GLOB" |
| 176 |
else |
| 177 |
echo "--- codesign (app): skipped, .app cleaned after bundling ---" |
| 178 |
fi |
| 179 |
if [ "$NOTARIZE" = true ]; then |
| 180 |
|
| 181 |
|
| 182 |
|
| 183 |
echo "--- notarize + staple (dmg) ---" |
| 184 |
xcrun notarytool submit "$DMG_PATH" \ |
| 185 |
--key "$API_KEY_PATH" --key-id "$API_KEY_ID" --issuer "$API_ISSUER_ID" --wait |
| 186 |
xcrun stapler staple "$DMG_PATH" |
| 187 |
xcrun stapler validate "$DMG_PATH" |
| 188 |
echo "--- spctl (Gatekeeper) ---" |
| 189 |
|
| 190 |
if ! spctl -a -vvv -t install "$DMG_PATH" 2>&1 | tee /dev/stderr | grep -q "source=Notarized Developer ID"; then |
| 191 |
echo "Error: DMG is not Gatekeeper-accepted as Notarized Developer ID." |
| 192 |
exit 1 |
| 193 |
fi |
| 194 |
else |
| 195 |
|
| 196 |
|
| 197 |
|
| 198 |
echo "--- codesign (signing authority / team) ---" |
| 199 |
codesign -dvvv "$APP_GLOB" 2>&1 | grep -E 'Authority|TeamIdentifier' || true |
| 200 |
echo "Note: --sign-only -- DMG is signed but NOT notarized; Gatekeeper will" |
| 201 |
echo " flag it on other Macs until you re-run without --sign-only post-transfer." |
| 202 |
fi |
| 203 |
fi |
| 204 |
|
| 205 |
|
| 206 |
mkdir -p "$DIST_DIR" |
| 207 |
cp "$DMG_PATH" "$DIST_DIR/" |
| 208 |
|
| 209 |
find target/release/bundle/macos -maxdepth 1 \( -name '*.app.tar.gz' -o -name '*.app.tar.gz.sig' \) \ |
| 210 |
-exec cp {} "$DIST_DIR/" \; 2>/dev/null || true |
| 211 |
|
| 212 |
echo "" |
| 213 |
echo "Done. GoingsOn macOS v$VERSION" |
| 214 |
echo "DMG staged at: $DIST_DIR/$(basename "$DMG_PATH")" |
| 215 |
echo "Next: clean-account DMG smoke test, then upload updater artifacts via the MNW dashboard." |
| 216 |
|