#!/bin/bash # Get the user's home directory USER_HOME="$HOME" # Set up Unreal Engine paths UE_ROOT="/Users/Shared/Epic Games/UE_5.5" UE_EDITOR="$UE_ROOT/Engine/Binaries/Mac/UnrealEditor.app/Contents/MacOS/UnrealEditor" UE_UAT="$UE_ROOT/Engine/Build/BatchFiles/RunUAT.command" # Set up project paths PROJECT_ROOT="$(pwd)" PROJECT_FILE="$PROJECT_ROOT/LuckyWorld.uproject" ARCHIVE_DIR="$PROJECT_ROOT/Builds" # Check for Developer ID certificate CERTIFICATE_NAME="" if [ -z "$CERTIFICATE_NAME" ]; then # Try to find a Developer ID Application certificate CERTIFICATE_NAME=$(security find-identity -v -p codesigning | grep "Developer ID Application" | head -1 | sed -E 's/.*"(Developer ID Application.*)"$/\1/') if [ -z "$CERTIFICATE_NAME" ]; then echo "⚠️ No Developer ID Application certificate found. Please specify a valid certificate name." echo "Available certificates:" security find-identity -v -p codesigning exit 1 else echo "🔑 Found Developer ID certificate: $CERTIFICATE_NAME" fi fi # Check for entitlements file if [ -f "$PROJECT_ROOT/LuckyWorld.entitlements" ]; then ENTITLEMENTS_FILE="$PROJECT_ROOT/LuckyWorld.entitlements" elif [ -f "$PROJECT_ROOT/LuckyRobots.entitlements" ]; then ENTITLEMENTS_FILE="$PROJECT_ROOT/LuckyRobots.entitlements" else echo "Warning: No entitlements file found. This might affect notarization." ENTITLEMENTS_FILE="" fi # For debugging: print paths and config echo "Project root: $PROJECT_ROOT" echo "Project file: $PROJECT_FILE" echo "Archive directory: $ARCHIVE_DIR" echo "Entitlements file: $ENTITLEMENTS_FILE" echo "Signing with certificate: $CERTIFICATE_NAME" # Clean up previous build artifacts rm -rf DerivedDataCache Intermediate Binaries Saved # Generate project files "$UE_ROOT/Engine/Build/BatchFiles/Mac/GenerateProjectFiles.sh" -project="$PROJECT_FILE" -game -engine # Run the build command "$UE_UAT" -ScriptsForProject="$PROJECT_FILE" Turnkey \ -command=VerifySdk \ -platform=Mac \ -UpdateIfNeeded \ -EditorIO \ -EditorIOPort=59484 \ -project="$PROJECT_FILE" \ BuildCookRun \ -nop4 \ -utf8output \ -cook \ -project="$PROJECT_FILE" \ -target=LuckyWorld \ -unrealexe="$UE_EDITOR" \ -platform=Mac \ -installed \ -stage \ -archive \ -package \ -build \ -iterativecooking \ -pak \ -iostore \ -compressed \ -prereqs \ -archivedirectory="$ARCHIVE_DIR" \ -CrashReporter \ -clientconfig=Shipping \ # -nocompile \ # -nocompileuat \ # -nocompileeditor \ # -skipbuildeditor \ # enable these if you want to test build without pak and iostore (you're just testing the build) # -skipiostore \ # -skippak \ (disable -pak and -iostore) echo "" echo "🦾 Build completed. Application path:" APP_PATH=$(find "$ARCHIVE_DIR" -name "*.app" -type d | head -n 1) echo "$APP_PATH" if [ -n "$APP_PATH" ]; then echo "" echo "🔍 Binary files that will need signing:" DYLIB_COUNT=$(find "$APP_PATH" -name "*.dylib" | wc -l) SO_COUNT=$(find "$APP_PATH" -name "*.so" | wc -l) FRAMEWORKS=$(find "$APP_PATH" -path "*.framework/*" -type f -perm +111 | wc -l) EXECUTABLES=$(find "$APP_PATH" -type f -perm +111 -not -path "*.framework/*" -not -name "*.dylib" -not -name "*.so" | wc -l) echo "- $DYLIB_COUNT .dylib libraries" echo "- $SO_COUNT .so libraries" echo "- $FRAMEWORKS framework executables" echo "- $EXECUTABLES other executables" echo "Total binary files: $((DYLIB_COUNT + SO_COUNT + FRAMEWORKS + EXECUTABLES))" echo "" echo "🔍 Checking for PhysX and other special libraries (often need special handling):" find "$APP_PATH" -name "*PhysX*" -o -name "*APEX*" fi # Update bundle ID in project settings echo "" echo "🔧 Updating bundle ID in UE config..." CONFIG_FILE="$PROJECT_ROOT/Config/DefaultGame.ini" if [ -f "$CONFIG_FILE" ]; then # Check if section exists or add it if grep -q "\[/Script/MacTargetPlatform\.MacTargetSettings\]" "$CONFIG_FILE"; then # Section exists, update the setting sed -i '' 's/BundleIdentifier=.*/BundleIdentifier=com.luckyrobots.luckyworld/g' "$CONFIG_FILE" else # Section doesn't exist, add it echo "" >> "$CONFIG_FILE" echo "[/Script/MacTargetPlatform.MacTargetSettings]" >> "$CONFIG_FILE" echo "BundleIdentifier=com.luckyrobots.luckyworld" >> "$CONFIG_FILE" fi echo "Updated bundle ID in project config" fi # Post-build process - set bundle ID echo "" echo "🔧 Performing post-build fix for bundle ID..." if [ -n "$APP_PATH" ]; then INFO_PLIST="$APP_PATH/Contents/Info.plist" if [ -f "$INFO_PLIST" ]; then echo "Setting bundle identifier to com.luckyrobots.luckyworld" /usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier com.luckyrobots.luckyworld" "$INFO_PLIST" echo "Updated bundle ID: $(/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "$INFO_PLIST")" else echo "⚠️ Info.plist not found at $INFO_PLIST" fi fi # Recursive signing function - signs all binary files function sign_recursively() { local app_path="$1" local entitlements_file="$2" local certificate="$3" local counter=0 local total=0 local failed=0 # First calculate total file count # Find all binary files (executables, dylibs, .so files, frameworks) echo "Scanning for binary files..." # Executable binary files (libraries, executables) binaries=$(find "$app_path" -type f \( -name "*.dylib" -o -name "*.so" -o -perm +111 \) | sort) total=$(echo "$binaries" | wc -l) echo "Found $total binary files to sign" # Sign helper binary files (in order of preference) echo "Signing all binary files (libraries and executables)..." echo "$binaries" | while read -r binary; do counter=$((counter + 1)) # Show progress every 20 files if [ $((counter % 20)) -eq 0 ] || [ $counter -eq 1 ] || [ $counter -eq $total ]; then echo "Progress: $counter/$total - Signing: $binary" fi # Skip if not a regular file (symbolic links etc) if [ ! -f "$binary" ]; then continue fi # Check file type file_info=$(file "$binary") # Only sign Mach-O files if ! echo "$file_info" | grep -q "Mach-O"; then continue fi if [[ "$binary" == *CrashReportClient* ]]; then echo "🛠️ Special handling for CrashReportClient: $binary" fi # Sign with timestamp and runtime options codesign --force --options runtime --deep --sign "$certificate" --timestamp --entitlements "$entitlements_file" "$binary" 2>&1 || { echo "⚠️ Failed to sign: $binary" failed=$((failed + 1)) } done # Show ENTITLEMENTS content for reference echo "Using entitlements file for signatures:" cat "$entitlements_file" # Find all nested apps and sign them nested_apps=$(find "$app_path" -name "*.app" -type d) if [ -n "$nested_apps" ]; then echo "Signing nested applications..." echo "$nested_apps" | while read -r nested_app; do if [ "$nested_app" != "$app_path" ]; then echo "Signing nested app: $nested_app" # Set Bundle ID in Info.plist if it exists before signing nested_info="$nested_app/Contents/Info.plist" if [ -f "$nested_info" ]; then echo "Setting bundle identifier for nested app" /usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier com.luckyrobots.luckyworld.nested" "$nested_info" 2>/dev/null || true fi codesign --force --options runtime --deep --sign "$certificate" --timestamp --entitlements "$entitlements_file" "$nested_app" 2>&1 || { echo "⚠️ Failed to sign nested app: $nested_app" failed=$((failed + 1)) } fi done fi # Sign the main application echo "Signing main application: $app_path" codesign --force --options runtime --deep --sign "$certificate" --timestamp --entitlements "$entitlements_file" "$app_path" 2>&1 || { echo "⚠️ Failed to sign main app: $app_path" failed=$((failed + 1)) } echo "✅ Signing completed: $counter files processed, $failed failures" # Check signing status echo "Verifying signatures..." codesign -vvv --deep --strict "$app_path" # Check Hardened Runtime and other security settings echo "Checking security settings (Hardened Runtime, etc.):" codesign -d --entitlements - "$app_path" | grep -i "runtime\|hardened\|security" # Check CrashReportClient specifically (problematic file) crash_reporter=$(find "$app_path" -path "*CrashReportClient.app/Contents/MacOS/CrashReportClient" -type f | head -1) if [ -n "$crash_reporter" ]; then echo "Checking CrashReportClient specifically:" codesign -d --entitlements - "$crash_reporter" | grep -i "runtime\|hardened\|security" fi } # Check libraries and perform post-processing if needed echo "" echo "🔍 Performing comprehensive signing and hardening of all binaries..." if [ -n "$APP_PATH" ] && [ -n "$ENTITLEMENTS_FILE" ]; then # Sign all binary files recursively sign_recursively "$APP_PATH" "$ENTITLEMENTS_FILE" "$CERTIFICATE_NAME" # Final signing of the main app bundle echo "Final signing of main app bundle" codesign --force --options runtime --deep --sign "$CERTIFICATE_NAME" --timestamp --entitlements "$ENTITLEMENTS_FILE" "$APP_PATH" echo "✅ All binaries signed successfully with Hardened Runtime enabled" # Prepare app for notarization echo "" echo "🔐 Preparing for notarization..." # Create a ZIP archive for notarization ZIP_PATH="$ARCHIVE_DIR/LuckyWorld.zip" echo "Creating ZIP archive for notarization: $ZIP_PATH" ditto -c -k --keepParent "$APP_PATH" "$ZIP_PATH" echo "✅ ZIP archive created for notarization at: $ZIP_PATH" echo "" echo "To notarize the app, run the following command:" echo "xcrun notarytool submit \"$ZIP_PATH\" --apple-id \"YOUR_APPLE_ID\" --password \"APP_SPECIFIC_PASSWORD\" --team-id \"YOUR_TEAM_ID\" --wait" echo "" else echo "❌ App path or entitlements file not found, cannot perform comprehensive signing" echo "App path: $APP_PATH" echo "Entitlements file: $ENTITLEMENTS_FILE" fi echo "" echo "✅ Build and post-processing completed successfully!"