#!/bin/zsh set -euo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" cd "$ROOT" command -v xcodebuild >/dev/null || { echo "Xcode is required" >&2; exit 1; } command -v hdiutil >/dev/null || { echo "hdiutil missing" >&2; exit 1; } # Use the certificate fingerprint to avoid ambiguous Keychain identities. IDENTITY="${SIGNING_IDENTITY:-$(security find-identity -v -p codesigning | awk '/Developer ID Application:/ {print $2; exit}')}" [[ -n "$IDENTITY" ]] || { echo "Developer ID Application signing identity required. Set SIGNING_IDENTITY." >&2; exit 1; } PROFILE="${NOTARY_PROFILE:-orion-notary}" [[ -n "$PROFILE" ]] || { echo "Set NOTARY_PROFILE to an existing notarytool keychain profile." >&2; exit 1; } DERIVED="$ROOT/.build" xcodebuild -project "$ROOT/PHPGuard.xcodeproj" -scheme PHPGuard -configuration Release -derivedDataPath "$DERIVED" CODE_SIGNING_ALLOWED=NO build APP="$DERIVED/Build/Products/Release/PHPGuard.app" [[ -d "$APP" ]] || { echo "Missing Release app" >&2; exit 1; } echo "=== VERIFY APPLICATION ICON ===" ASSET_CATALOG="$APP/Contents/Resources/Assets.car" if [[ ! -f "$ASSET_CATALOG" ]]; then echo "ERROR: Compiled asset catalog missing" >&2 exit 1 fi APP_ICON_NAME=$(/usr/libexec/PlistBuddy -c "Print :CFBundleIconName" "$APP/Contents/Info.plist" 2>/dev/null || true) APP_ICON_FILE=$(/usr/libexec/PlistBuddy -c "Print :CFBundleIconFile" "$APP/Contents/Info.plist" 2>/dev/null || true) if [[ -n "$APP_ICON_NAME" ]]; then echo "Application icon asset: $APP_ICON_NAME" elif [[ -n "$APP_ICON_FILE" ]]; then echo "Application icon file: $APP_ICON_FILE" else echo "WARNING: No application icon key in Info.plist." echo "Checking Xcode asset catalog configuration..." ICON_SETTING=$(xcodebuild -project "$ROOT/PHPGuard.xcodeproj" -scheme PHPGuard -configuration Release -showBuildSettings 2>/dev/null | sed -n 's/^[[:space:]]*ASSETCATALOG_COMPILER_APPICON_NAME = //p' | head -1) if [[ -z "$ICON_SETTING" ]]; then echo "ERROR: No application icon configured in Xcode." >&2 exit 1 fi echo "Configured icon asset: $ICON_SETTING" fi echo "Compiled asset catalog present." echo "Application icon configuration verified." VERSION="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleShortVersionString' "$APP/Contents/Info.plist")" mkdir -p "$ROOT/dist" STAGING="$(mktemp -d)" trap 'rm -rf "$STAGING"' EXIT cp -R "$APP" "$STAGING/PHPGuard.app" # No --deep signing: sign embedded executables individually if added in future. codesign --force --options runtime --timestamp --sign "$IDENTITY" "$STAGING/PHPGuard.app" codesign --verify --strict --verbose=2 "$STAGING/PHPGuard.app" DMG="$ROOT/dist/PHPGuard-$VERSION.dmg" rm -f "$DMG" ln -s /Applications "$STAGING/Applications" # Include a branded installer graphic in the disk image. INSTALLER_ICON="$ROOT/PHPGuard/Assets.xcassets/InstallerIcon.imageset/icon.png" if [[ -f "$INSTALLER_ICON" ]]; then mkdir -p "$STAGING/.background" cp "$INSTALLER_ICON" "$STAGING/.background/PHPGuard-Installer.png" fi # Generate a proper macOS installer icon. INSTALLER_PNG="$ROOT/PHPGuard/Assets.xcassets/InstallerIcon.imageset/icon.png" ICONSET="$ROOT/dist/PHPGuardInstaller.iconset" INSTALLER_ICNS="${TMPDIR:-/tmp}/PHPGuardInstaller-$$.icns" if [[ -f "$INSTALLER_PNG" ]]; then rm -rf "$ICONSET" mkdir -p "$ICONSET" for size in 16 32 128 256 512; do sips -s format png -z "$size" "$size" "$INSTALLER_PNG" --out "$ICONSET/icon_${size}x${size}.png" >/dev/null double=$((size * 2)) sips -s format png -z "$double" "$double" "$INSTALLER_PNG" --out "$ICONSET/icon_${size}x${size}@2x.png" >/dev/null done iconutil -c icns "$ICONSET" -o "$INSTALLER_ICNS" rm -rf "$ICONSET" else echo "ERROR: Installer artwork missing" >&2 exit 1 fi hdiutil create -volname "PHPGuard $VERSION" -srcfolder "$STAGING" -ov -format UDZO "$DMG" # Apply custom Finder icon to the DMG. # Apply the DMG Finder icon using native macOS tools. echo "=== APPLY DMG ICON ===" # Native macOS tools. No Homebrew dependencies. REZ="$(xcrun --find Rez)" SETFILE="$(xcrun --find SetFile)" [[ -f "$INSTALLER_ICNS" ]] || { echo "ERROR: Installer .icns missing" >&2 exit 1 } # Create an icon resource containing the .icns data. ICON_RESOURCE="${TMPDIR:-/tmp}/PHPGuardInstaller-$$.r" python3 - "$INSTALLER_ICNS" "$ICON_RESOURCE" <<'ICONPY' import sys from pathlib import Path icon = Path(sys.argv[1]).read_bytes() if not icon.startswith(b"icns"): raise SystemExit("ERROR: Invalid .icns file") hexdata = icon.hex().upper() with open(sys.argv[2], "w") as f: f.write("data 'icns' (-16455) {\n") for i in range(0, len(hexdata), 64): f.write(' $"' + hexdata[i:i+64] + '"\n') f.write("};\n") ICONPY # Attach icon resource to the DMG. "$REZ" -append "$ICON_RESOURCE" -o "$DMG" # Set the Finder custom-icon flag. "$SETFILE" -a C "$DMG" rm -f "$ICON_RESOURCE" echo "Custom DMG icon applied." xcrun notarytool submit "$DMG" --keychain-profile "$PROFILE" --wait xcrun stapler staple "$DMG" xcrun stapler validate "$DMG" # Keep only the final release DMG in dist. echo "=== CLEAN RELEASE DIRECTORY ===" find "$ROOT/dist" -maxdepth 1 -type f ! -name "$(basename "$DMG")" -delete echo "Release ready: $DMG"