2025-03-27 16:47:31 -05:00
#!/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 ) "
2025-04-10 07:17:18 -05:00
PROJECT_FILE = " $PROJECT_ROOT /LuckyWorld.uproject "
2025-03-27 16:47:31 -05:00
ARCHIVE_DIR = " $PROJECT_ROOT /Builds "
2025-04-14 21:20:13 +02:00
# 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
2025-04-13 20:54:08 +02:00
# 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 "
2025-04-14 21:20:13 +02:00
echo " Signing with certificate: $CERTIFICATE_NAME "
2025-04-13 20:54:08 +02:00
# Clean up previous build artifacts
2025-04-10 07:17:18 -05:00
rm -rf DerivedDataCache Intermediate Binaries Saved
2025-04-13 20:54:08 +02:00
# Generate project files
2025-04-10 07:17:18 -05:00
" $UE_ROOT /Engine/Build/BatchFiles/Mac/GenerateProjectFiles.sh " -project= " $PROJECT_FILE " -game -engine
2025-04-13 20:54:08 +02:00
2025-03-27 16:47:31 -05:00
# 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 " \
2025-04-10 07:17:18 -05:00
-target= LuckyWorld \
2025-03-27 16:47:31 -05:00
-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 \
2025-04-13 20:54:08 +02:00
# -skippak \ (disable -pak and -iostore)
echo ""
2025-04-14 18:07:06 +02:00
echo "🦾 Build completed. Application path:"
2025-04-13 20:54:08 +02:00
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
2025-04-14 19:53:37 +02:00
2025-04-14 21:20:13 +02:00
# Update bundle ID in project settings
2025-04-14 19:53:37 +02:00
echo ""
echo "🔧 Updating bundle ID in UE config..."
CONFIG_FILE = " $PROJECT_ROOT /Config/DefaultGame.ini "
if [ -f " $CONFIG_FILE " ] ; then
2025-04-14 21:20:13 +02:00
# Check if section exists or add it
2025-04-14 19:53:37 +02:00
if grep -q "\[/Script/MacTargetPlatform\.MacTargetSettings\]" " $CONFIG_FILE " ; then
2025-04-14 21:20:13 +02:00
# Section exists, update the setting
2025-04-14 19:53:37 +02:00
sed -i '' 's/BundleIdentifier=.*/BundleIdentifier=com.luckyrobots.luckyworld/g' " $CONFIG_FILE "
else
2025-04-14 21:20:13 +02:00
# Section doesn't exist, add it
2025-04-14 19:53:37 +02:00
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
2025-04-14 21:20:13 +02:00
# Post-build process - set bundle ID
2025-04-14 19:53:37 +02:00
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
2025-04-14 21:20:13 +02:00
# Recursive signing function - signs all binary files
2025-04-14 19:56:08 +02:00
function sign_recursively( ) {
local app_path = " $1 "
local entitlements_file = " $2 "
2025-04-14 21:20:13 +02:00
local certificate = " $3 "
2025-04-14 19:56:08 +02:00
local counter = 0
local total = 0
local failed = 0
2025-04-14 21:20:13 +02:00
# First calculate total file count
# Find all binary files (executables, dylibs, .so files, frameworks)
2025-04-14 19:56:08 +02:00
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 "
2025-04-14 21:20:13 +02:00
# Sign helper binary files (in order of preference)
2025-04-14 19:56:08 +02:00
echo "Signing all binary files (libraries and executables)..."
echo " $binaries " | while read -r binary; do
counter = $(( counter + 1 ))
2025-04-14 21:20:13 +02:00
# Show progress every 20 files
2025-04-14 19:56:08 +02:00
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
2025-04-14 21:20:13 +02:00
# Check file type
2025-04-14 19:56:08 +02:00
file_info = $( file " $binary " )
2025-04-14 21:20:13 +02:00
# Only sign Mach-O files
2025-04-14 19:56:08 +02:00
if ! echo " $file_info " | grep -q "Mach-O" ; then
continue
2025-04-14 19:53:37 +02:00
fi
2025-04-14 19:56:08 +02:00
if [ [ " $binary " = = *CrashReportClient* ] ] ; then
echo " 🛠️ Special handling for CrashReportClient: $binary "
fi
2025-04-14 21:20:13 +02:00
# Sign with timestamp and runtime options
codesign --force --options runtime --deep --sign " $certificate " --timestamp --entitlements " $entitlements_file " " $binary " 2>& 1 || {
2025-04-14 19:56:08 +02:00
echo " ⚠️ Failed to sign: $binary "
failed = $(( failed + 1 ))
}
2025-04-14 19:53:37 +02:00
done
2025-04-14 21:20:13 +02:00
# Show ENTITLEMENTS content for reference
2025-04-14 19:56:08 +02:00
echo "Using entitlements file for signatures:"
cat " $entitlements_file "
2025-04-14 21:20:13 +02:00
# Find all nested apps and sign them
2025-04-14 19:56:08 +02:00
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 "
2025-04-14 21:20:13 +02:00
# Set Bundle ID in Info.plist if it exists before signing
2025-04-14 19:56:08 +02:00
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
2025-04-14 21:20:13 +02:00
codesign --force --options runtime --deep --sign " $certificate " --timestamp --entitlements " $entitlements_file " " $nested_app " 2>& 1 || {
2025-04-14 19:56:08 +02:00
echo " ⚠️ Failed to sign nested app: $nested_app "
failed = $(( failed + 1 ))
}
fi
done
fi
2025-04-14 21:20:13 +02:00
# Sign the main application
2025-04-14 19:56:08 +02:00
echo " Signing main application: $app_path "
2025-04-14 21:20:13 +02:00
codesign --force --options runtime --deep --sign " $certificate " --timestamp --entitlements " $entitlements_file " " $app_path " 2>& 1 || {
2025-04-14 19:56:08 +02:00
echo " ⚠️ Failed to sign main app: $app_path "
failed = $(( failed + 1 ))
}
echo " ✅ Signing completed: $counter files processed, $failed failures "
2025-04-14 21:20:13 +02:00
# Check signing status
2025-04-14 19:56:08 +02:00
echo "Verifying signatures..."
codesign -vvv --deep --strict " $app_path "
2025-04-14 21:20:13 +02:00
# Check Hardened Runtime and other security settings
2025-04-14 19:56:08 +02:00
echo "Checking security settings (Hardened Runtime, etc.):"
codesign -d --entitlements - " $app_path " | grep -i "runtime\|hardened\|security"
2025-04-14 21:20:13 +02:00
# Check CrashReportClient specifically (problematic file)
2025-04-14 19:56:08 +02:00
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"
2025-04-14 19:53:37 +02:00
fi
2025-04-14 19:56:08 +02:00
}
2025-04-14 21:20:13 +02:00
# Check libraries and perform post-processing if needed
2025-04-14 19:56:08 +02:00
echo ""
echo "🔍 Performing comprehensive signing and hardening of all binaries..."
if [ -n " $APP_PATH " ] && [ -n " $ENTITLEMENTS_FILE " ] ; then
2025-04-14 21:20:13 +02:00
# Sign all binary files recursively
sign_recursively " $APP_PATH " " $ENTITLEMENTS_FILE " " $CERTIFICATE_NAME "
2025-04-14 19:56:08 +02:00
2025-04-14 21:20:13 +02:00
# Final signing of the main app bundle
2025-04-14 19:56:08 +02:00
echo "Final signing of main app bundle"
2025-04-14 21:20:13 +02:00
codesign --force --options runtime --deep --sign " $CERTIFICATE_NAME " --timestamp --entitlements " $ENTITLEMENTS_FILE " " $APP_PATH "
2025-04-14 19:56:08 +02:00
echo "✅ All binaries signed successfully with Hardened Runtime enabled"
2025-04-14 21:20:13 +02:00
# 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 ""
2025-04-14 19:56:08 +02:00
else
echo "❌ App path or entitlements file not found, cannot perform comprehensive signing"
echo " App path: $APP_PATH "
echo " Entitlements file: $ENTITLEMENTS_FILE "
2025-04-14 19:53:37 +02:00
fi
echo ""
echo "✅ Build and post-processing completed successfully!"