149 lines
5.1 KiB
Bash
Executable File
149 lines
5.1 KiB
Bash
Executable File
#!/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"
|