diff --git a/.gitea/actions/linux-build/action.yml b/.gitea/actions/linux-build/action.yml
new file mode 100644
index 00000000..f9cf4604
--- /dev/null
+++ b/.gitea/actions/linux-build/action.yml
@@ -0,0 +1,46 @@
+name: 'Linux Build Steps'
+description: 'Build Linux application'
+
+runs:
+ using: "composite"
+ steps:
+ - name: Setup environment
+ run: |
+ # Set environment variables for Unreal Engine
+ echo "UE_ROOT=E:/Games/UE_5.5" >> $GITHUB_ENV
+
+ # Set environment variables for Linux toolchain (needed for cross-compilation)
+ $env:LINUX_MULTIARCH_ROOT="C:/UnrealToolchains/v23_clang-18.1.0-rockylinux8"
+ echo "LINUX_MULTIARCH_ROOT=${LINUX_MULTIARCH_ROOT}" >> $GITHUB_ENV
+
+ # Create directories for builds
+ if (!(Test-Path "Builds/Linux")) { New-Item -ItemType Directory -Path "Builds/Linux" -Force }
+ if (!(Test-Path "PackagedReleases")) { New-Item -ItemType Directory -Path "PackagedReleases" -Force }
+ shell: pwsh
+
+ - name: Build for Linux
+ run: |
+ # Chmod command doesn't exist in Windows, use PowerShell to run the bash script
+ & 'C:\Program Files\Git\bin\bash.exe' -c "./scripts/linux_build.sh"
+ shell: pwsh
+
+ - name: Package Linux build
+ run: |
+ echo "Packaging Linux build..."
+ if [ -d "Builds/Linux" ]; then
+ cd Builds/Linux
+ zip -r ../../PackagedReleases/LuckyRobots-Linux.zip .
+ cd ../..
+ fi
+
+ echo "=== Packaged Linux release ==="
+ ls -la PackagedReleases/
+ shell: bash
+
+ - name: Upload Linux Build Artifact
+ uses: actions/upload-artifact@v3
+ if: success() && hashFiles('PackagedReleases/LuckyRobots-Linux.zip') != ''
+ with:
+ name: LuckyRobots-Linux
+ path: PackagedReleases/LuckyRobots-Linux.zip
+ retention-days: 365
\ No newline at end of file
diff --git a/.gitea/actions/macos-build/action.yml b/.gitea/actions/macos-build/action.yml
new file mode 100644
index 00000000..62c94c09
--- /dev/null
+++ b/.gitea/actions/macos-build/action.yml
@@ -0,0 +1,132 @@
+name: 'macOS Build Steps'
+description: 'Build, sign and notarize macOS application'
+
+inputs:
+ apple_team_id:
+ description: 'Apple Team ID for signing'
+ required: true
+ apple_certificate_base64:
+ description: 'Base64-encoded certificate file'
+ required: true
+ apple_certificate_password:
+ description: 'Password for certificate file'
+ required: true
+ api_key_path:
+ description: 'Base64-encoded API key file'
+ required: true
+ api_key_id:
+ description: 'API Key ID'
+ required: true
+ api_key_issuer_id:
+ description: 'API Key Issuer ID'
+ required: true
+
+runs:
+ using: "composite"
+ steps:
+ - name: Setup environment
+ run: |
+ # Use the correct path where Unreal Engine is installed
+ UE_PATH="/Users/Shared/Epic Games/UE_5.5"
+
+ if [ ! -d "$UE_PATH" ]; then
+ echo "Error: Unreal Engine is not installed in the expected location"
+ echo "Please ensure Unreal Engine is installed at $UE_PATH"
+ exit 1
+ fi
+
+ # Create directories for builds
+ mkdir -p Builds/Mac
+ mkdir -p PackagedReleases
+
+ echo "Using Unreal Engine 5.5"
+ shell: bash
+
+ - name: Build for macOS
+ run: |
+ chmod +x ./scripts/mac_build.sh
+ ./scripts/mac_build.sh
+ shell: bash
+
+ - name: Setup for Signing
+ id: setup-signing
+ if: ${{ success() }}
+ env:
+ API_KEY_PATH: ${{ inputs.api_key_path }}
+ run: |
+ # Create output directory
+ mkdir -p PackagedReleases
+
+ # Decode the API key from Base64 secret
+ echo "$API_KEY_PATH" | base64 --decode > api_key.p8
+ echo "api_key_file=$(pwd)/api_key.p8" >> $GITHUB_OUTPUT
+
+ # Find app bundle
+ APP_PATH=$(find Builds -type d -name "*.app" | head -1)
+
+ if [ -z "$APP_PATH" ]; then
+ # Look for a directory that might be a bundle but not named .app
+ APP_PATH=$(find Builds -mindepth 1 -maxdepth 1 -type d | head -1)
+ if [ -z "$APP_PATH" ]; then
+ echo "No build directory found, cannot continue"
+ exit 1
+ fi
+ fi
+
+ echo "Found app path: $APP_PATH"
+ echo "app_path=$APP_PATH" >> $GITHUB_OUTPUT
+ shell: bash
+
+ - name: Sign macOS App
+ uses: lando/code-sign-action@v3
+ id: sign-app
+ with:
+ file: ${{ steps.setup-signing.outputs.app_path }}
+ certificate-data: ${{ inputs.apple_certificate_base64 }}
+ certificate-password: ${{ inputs.apple_certificate_password }}
+ certificate-id: ${{ inputs.apple_team_id }}
+ options: --force --options runtime --deep --timestamp --entitlements ./LuckyRobots.entitlements
+
+ - name: Notarize macOS App
+ run: |
+ # Create a temporary file for notarization
+ APP_PATH="${{ steps.setup-signing.outputs.app_path }}"
+ NOTARIZE_APP_PATH="./LuckyRobots-notarize.zip"
+ ditto -c -k --keepParent "$APP_PATH" "$NOTARIZE_APP_PATH"
+
+ API_KEY_FILE="${{ steps.setup-signing.outputs.api_key_file }}"
+
+ # Submit for notarization using API key
+ echo "Submitting for notarization with API key..."
+ xcrun notarytool submit "$NOTARIZE_APP_PATH" --key "$API_KEY_FILE" --key-id "${{ inputs.api_key_id }}" --issuer "${{ inputs.api_key_issuer_id }}" --wait
+
+ # Staple the ticket to the application
+ xcrun stapler staple "$APP_PATH"
+
+ # Clean up the API key file
+ rm -f "$API_KEY_FILE"
+ rm -f "$NOTARIZE_APP_PATH"
+ shell: bash
+
+ - name: Package macOS App
+ run: |
+ # Package the signed and notarized app
+ APP_PATH="${{ steps.setup-signing.outputs.app_path }}"
+ APP_NAME=$(basename "$APP_PATH")
+ DIR_PATH=$(dirname "$APP_PATH")
+
+ echo "Creating final package..."
+ (cd "$DIR_PATH" && zip -r "../../PackagedReleases/LuckyRobots-macOS.zip" "$APP_NAME")
+ echo "Created packaged release: PackagedReleases/LuckyRobots-macOS.zip"
+
+ echo "Packaged releases:"
+ ls -la PackagedReleases/
+ shell: bash
+
+ - name: Upload macOS Build Artifact
+ uses: actions/upload-artifact@v3
+ if: success()
+ with:
+ name: LuckyRobots-macOS
+ path: PackagedReleases/LuckyRobots-macOS.zip
+ retention-days: 365
\ No newline at end of file
diff --git a/.gitea/actions/macos-notarize/action.yml b/.gitea/actions/macos-notarize/action.yml
new file mode 100644
index 00000000..e77e590b
--- /dev/null
+++ b/.gitea/actions/macos-notarize/action.yml
@@ -0,0 +1,704 @@
+name: macOS Notarize
+description: 'Signs and notarizes a macOS application with Apple certificates'
+author: moersoy
+
+inputs:
+ app-path:
+ description: 'Path to the .app bundle to sign'
+ required: true
+ entitlements-file:
+ description: 'Path to entitlements file to use for signing'
+ required: false
+ default: ''
+ team-id:
+ description: 'Apple Developer Team ID'
+ required: true
+ certificate-base64:
+ description: 'Base64-encoded certificate (P12 file)'
+ required: true
+ certificate-password:
+ description: 'Certificate password'
+ required: true
+ notarization-method:
+ description: 'Method to use for notarization (api-key or app-password)'
+ required: false
+ default: 'api-key'
+ app-password:
+ description: 'App-specific password for Apple ID (required if using app-password method)'
+ required: false
+ default: ''
+ apple-id:
+ description: 'Apple ID email (required if using app-password method)'
+ required: false
+ default: ''
+ notary-api-key-id:
+ description: 'App Store Connect API Key ID (required if using api-key method)'
+ required: false
+ default: ''
+ notary-api-key-issuer-id:
+ description: 'App Store Connect API Key Issuer ID (required if using api-key method)'
+ required: false
+ default: ''
+ notary-api-key-path:
+ description: 'App Store Connect API Key file content (base64 encoded) (required if using api-key method)'
+ required: false
+ default: ''
+ bundle-id:
+ description: 'Bundle ID of the app'
+ required: false
+ default: ''
+ fallback-to-adhoc:
+ description: 'Fallback to ad-hoc signing if no certificate is available'
+ required: false
+ default: 'true'
+
+outputs:
+ signed:
+ description: 'Signing status (true, ad-hoc, none)'
+ value: ${{ steps.set-outputs.outputs.signed }}
+ notarized:
+ description: 'Notarization status (true, false)'
+ value: ${{ steps.set-outputs.outputs.notarized }}
+ app-path:
+ description: 'Path to the signed app bundle'
+ value: ${{ steps.set-outputs.outputs.app-path }}
+ zip-path:
+ description: 'Path to the packaged .ZIP file'
+ value: ${{ steps.set-outputs.outputs.zip-path }}
+ package-path:
+ description: 'Path to the packaged .DMG file'
+ value: ${{ steps.set-outputs.outputs.package-path }}
+
+runs:
+ using: "composite"
+ steps:
+ - name: Setup debug environment
+ run: |
+ # Create debug directory if env variable is set
+ if [[ -n "$DEBUG_LOG_PATH" ]]; then
+ mkdir -p "$(dirname "$DEBUG_LOG_PATH")"
+ touch "$DEBUG_LOG_PATH"
+ echo "Debug logging enabled to: $DEBUG_LOG_PATH" | tee -a "$DEBUG_LOG_PATH"
+ fi
+
+ # Define a debug function
+ debug_log() {
+ echo "DEBUG: $1"
+ if [[ -n "$DEBUG_LOG_PATH" ]]; then
+ echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$DEBUG_LOG_PATH"
+ fi
+ }
+ # Export the function for use in subsequent steps
+ export -f debug_log
+
+ debug_log "Starting macOS notarize action"
+ debug_log "App path: ${{ inputs.app-path }}"
+ debug_log "Team ID: ${{ inputs.team-id }}"
+ debug_log "Notarization method: ${{ inputs.notarization-method }}"
+ debug_log "Bundle ID: ${{ inputs.bundle-id }}"
+ shell: bash
+
+ - name: Set up variables
+ id: setup
+ run: |
+ # Debugging info
+ debug_log "Setting up variables"
+
+ # Generate unique name for keychain
+ KEYCHAIN_NAME="build-keychain-$(uuidgen)"
+ KEYCHAIN_PASSWORD="$(uuidgen)"
+ echo "KEYCHAIN_NAME=$KEYCHAIN_NAME" >> $GITHUB_ENV
+ echo "KEYCHAIN_PASSWORD=$KEYCHAIN_PASSWORD" >> $GITHUB_ENV
+
+ # Set paths
+ echo "APP_PATH=${{ inputs.app-path }}" >> $GITHUB_ENV
+
+ # Generate working directory for temp files
+ WORK_DIR="$(mktemp -d)"
+ echo "WORK_DIR=$WORK_DIR" >> $GITHUB_ENV
+
+ # Set bundle id (from input or extract from app)
+ if [[ -n "${{ inputs.bundle-id }}" ]]; then
+ BUNDLE_ID="${{ inputs.bundle-id }}"
+ else
+ BUNDLE_ID=$(/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "${{ inputs.app-path }}/Contents/Info.plist")
+ fi
+ echo "BUNDLE_ID=$BUNDLE_ID" >> $GITHUB_ENV
+
+ # Get app name from bundle path
+ APP_NAME=$(basename "${{ inputs.app-path }}" .app)
+ echo "APP_NAME=$APP_NAME" >> $GITHUB_ENV
+
+ # Set output directory
+ OUTPUT_DIR="$(pwd)/PackagedReleases"
+ mkdir -p "$OUTPUT_DIR"
+ echo "OUTPUT_DIR=$OUTPUT_DIR" >> $GITHUB_ENV
+
+ # Set package paths
+ ZIP_PATH="$OUTPUT_DIR/${APP_NAME}.zip"
+ DMG_PATH="$OUTPUT_DIR/${APP_NAME}.dmg"
+ echo "ZIP_PATH=$ZIP_PATH" >> $GITHUB_ENV
+ echo "DMG_PATH=$DMG_PATH" >> $GITHUB_ENV
+
+ # Set notarization variables based on method
+ if [[ "${{ inputs.notarization-method }}" == "api-key" ]]; then
+ echo "Using API key method for notarization"
+
+ # Create API key file
+ API_KEY_FILE="$WORK_DIR/api_key.p8"
+ echo "${{ inputs.notary-api-key-path }}" | base64 --decode > "$API_KEY_FILE"
+ echo "API_KEY_FILE=$API_KEY_FILE" >> $GITHUB_ENV
+
+ # Verify API key file exists
+ if [[ ! -f "$API_KEY_FILE" ]]; then
+ debug_log "ERROR: API key file could not be created"
+ exit 1
+ fi
+
+ debug_log "API key file created at: $API_KEY_FILE"
+ debug_log "API key ID: ${{ inputs.notary-api-key-id }}"
+ debug_log "API key issuer ID: ${{ inputs.notary-api-key-issuer-id }}"
+ else
+ echo "Using app-specific password method for notarization"
+ debug_log "Apple ID: ${{ inputs.apple-id }}"
+ fi
+ shell: bash
+
+ - name: Setup keychain
+ id: setup-keychain
+ run: |
+ debug_log "Setting up keychain"
+
+ # Create temporary keychain
+ security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
+ security default-keychain -s "$KEYCHAIN_NAME"
+ security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
+ security set-keychain-settings -t 3600 -u "$KEYCHAIN_NAME"
+
+ # Create certificate file
+ CERTIFICATE_PATH="$WORK_DIR/certificate.p12"
+ echo "${{ inputs.certificate-base64 }}" | base64 --decode > "$CERTIFICATE_PATH"
+
+ # Add to keychain
+ debug_log "Importing certificate into keychain"
+ security import "$CERTIFICATE_PATH" -k "$KEYCHAIN_NAME" -P "${{ inputs.certificate-password }}" -T /usr/bin/codesign
+
+ # Allow codesign to access keychain items
+ security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
+
+ # Verify certificate was imported
+ security find-identity -v "$KEYCHAIN_NAME" | grep "Developer ID Application"
+ IDENTITY_RESULT=$?
+
+ if [ $IDENTITY_RESULT -eq 0 ]; then
+ debug_log "Certificate imported successfully"
+ SIGNING_IDENTITY="Developer ID Application: ${{ inputs.team-id }}"
+ echo "SIGNING_IDENTITY=$SIGNING_IDENTITY" >> $GITHUB_ENV
+ echo "CERTIFICATE_AVAILABLE=true" >> $GITHUB_ENV
+ else
+ debug_log "WARNING: No Developer ID Application certificate found"
+ if [[ "${{ inputs.fallback-to-adhoc }}" == "true" ]]; then
+ debug_log "Falling back to ad-hoc signing"
+ echo "CERTIFICATE_AVAILABLE=adhoc" >> $GITHUB_ENV
+ else
+ debug_log "Not falling back to ad-hoc signing as specified"
+ echo "CERTIFICATE_AVAILABLE=false" >> $GITHUB_ENV
+ fi
+ fi
+ shell: bash
+
+ - name: Sign application
+ id: sign-app
+ run: |
+ debug_log "Starting application signing process"
+
+ # Check if certificate is available
+ if [[ "$CERTIFICATE_AVAILABLE" == "false" ]]; then
+ debug_log "No certificate available and fallback disabled. Skipping signing."
+ echo "SIGNING_RESULT=none" >> $GITHUB_ENV
+ exit 0
+ fi
+
+ # Sign the app
+ if [[ "$CERTIFICATE_AVAILABLE" == "true" ]]; then
+ debug_log "Signing with Developer ID certificate"
+
+ # First remove existing signatures
+ debug_log "Removing existing signatures..."
+ codesign --remove-signature "$APP_PATH" || true
+
+ # Sign all dynamic libraries and frameworks
+ debug_log "Signing embedded binaries and frameworks..."
+ find "$APP_PATH/Contents/MacOS" -type f -name "*.dylib" -exec codesign --force --timestamp --options runtime --entitlements "$ENTITLEMENTS_PATH" --sign "$SIGNING_IDENTITY" {} \;
+ find "$APP_PATH/Contents/Frameworks" -type f -depth 1 -exec codesign --force --timestamp --options runtime --entitlements "$ENTITLEMENTS_PATH" --sign "$SIGNING_IDENTITY" {} \;
+ find "$APP_PATH/Contents/Frameworks" -name "*.framework" -exec codesign --force --timestamp --options runtime --entitlements "$ENTITLEMENTS_PATH" --sign "$SIGNING_IDENTITY" {} \;
+
+ # Sign all executables
+ debug_log "Signing executables..."
+ find "$APP_PATH/Contents/MacOS" -type f -exec codesign --force --timestamp --options runtime --entitlements "$ENTITLEMENTS_PATH" --sign "$SIGNING_IDENTITY" {} \;
+
+ # Sign app bundle
+ debug_log "Signing main app bundle..."
+ codesign --force --timestamp --options runtime --entitlements "$ENTITLEMENTS_PATH" --sign "$SIGNING_IDENTITY" "$APP_PATH"
+
+ SIGN_RESULT=$?
+ if [ $SIGN_RESULT -eq 0 ]; then
+ debug_log "App signed successfully with Developer ID"
+ echo "SIGNING_RESULT=true" >> $GITHUB_ENV
+ else
+ debug_log "App signing failed with Developer ID"
+ echo "SIGNING_RESULT=false" >> $GITHUB_ENV
+ exit 1
+ fi
+
+ elif [[ "$CERTIFICATE_AVAILABLE" == "adhoc" ]]; then
+ debug_log "Signing with ad-hoc identity (not suitable for distribution)"
+
+ # Remove existing signatures
+ codesign --remove-signature "$APP_PATH" || true
+
+ # Sign with ad-hoc identity
+ codesign --force --timestamp --options runtime --entitlements "$ENTITLEMENTS_PATH" --sign - "$APP_PATH"
+
+ SIGN_RESULT=$?
+ if [ $SIGN_RESULT -eq 0 ]; then
+ debug_log "App signed successfully with ad-hoc identity"
+ echo "SIGNING_RESULT=ad-hoc" >> $GITHUB_ENV
+ else
+ debug_log "App signing failed with ad-hoc identity"
+ echo "SIGNING_RESULT=false" >> $GITHUB_ENV
+ exit 1
+ fi
+ else
+ debug_log "Unexpected certificate state. Skipping signing."
+ echo "SIGNING_RESULT=none" >> $GITHUB_ENV
+ fi
+
+ # Verify signing
+ debug_log "Verifying app signature..."
+ codesign -dvv "$APP_PATH"
+ shell: bash
+
+ - name: Verify notarization and stapling
+ id: verify-notarization
+ if: env.SIGNING_RESULT == 'true'
+ run: |
+ debug_log "Verifying app signature and code requirements before notarization"
+
+ # Verify code signature
+ codesign --verify --verbose "$APP_PATH"
+ if [ $? -ne 0 ]; then
+ debug_log "Error: App signature verification failed"
+ # Don't exit, just log the error
+ else
+ debug_log "App signature verification passed"
+ fi
+
+ # Check app for code requirements
+ codesign --display --requirements "$APP_PATH"
+ if [ $? -ne 0 ]; then
+ debug_log "Error: App doesn't meet requirements"
+ # Don't exit, just log the error
+ else
+ debug_log "App meets code requirements"
+ fi
+ shell: bash
+
+ - name: Notarize application
+ id: notarize-app
+ if: env.SIGNING_RESULT == 'true'
+ run: |
+ debug_log "Starting notarization process"
+
+ # Create ZIP for notarization
+ debug_log "Creating ZIP archive for notarization"
+ ditto -c -k --keepParent "$APP_PATH" "$ZIP_PATH"
+ if [ $? -ne 0 ]; then
+ debug_log "Error creating ZIP archive"
+ echo "NOTARIZATION_RESULT=false" >> $GITHUB_ENV
+ exit 1
+ fi
+
+ # Notarize the app
+ if [[ "${{ inputs.notarization-method }}" == "api-key" ]]; then
+ debug_log "Notarizing with API key method"
+
+ # Submit for notarization
+ debug_log "Submitting app for notarization..."
+ xcrun notarytool submit "$ZIP_PATH" \
+ --key "$API_KEY_FILE" \
+ --key-id "${{ inputs.notary-api-key-id }}" \
+ --issuer "${{ inputs.notary-api-key-issuer-id }}" \
+ --wait > "$WORK_DIR/notarization_output.txt" 2>&1
+
+ cat "$WORK_DIR/notarization_output.txt" | tee -a "$DEBUG_LOG_PATH"
+
+ REQUEST_STATUS=$(grep -o "status: .*" "$WORK_DIR/notarization_output.txt" | cut -d ' ' -f2)
+
+ if [[ "$REQUEST_STATUS" == "Accepted" ]]; then
+ debug_log "Notarization successful"
+ echo "NOTARIZATION_RESULT=true" >> $GITHUB_ENV
+ else
+ debug_log "Notarization failed or timed out"
+ cat "$WORK_DIR/notarization_output.txt"
+ echo "NOTARIZATION_RESULT=false" >> $GITHUB_ENV
+ fi
+
+ else
+ debug_log "Notarizing with app-specific password method"
+
+ # Submit for notarization
+ debug_log "Submitting app for notarization..."
+ xcrun altool --notarize-app \
+ --primary-bundle-id "$BUNDLE_ID" \
+ --username "${{ inputs.apple-id }}" \
+ --password "${{ inputs.app-password }}" \
+ --file "$ZIP_PATH" \
+ > "$WORK_DIR/notarization_output.txt" 2>&1
+
+ cat "$WORK_DIR/notarization_output.txt" | tee -a "$DEBUG_LOG_PATH"
+
+ REQUEST_UUID=$(grep -o "RequestUUID = .*" "$WORK_DIR/notarization_output.txt" | cut -d ' ' -f3)
+
+ if [[ -n "$REQUEST_UUID" ]]; then
+ debug_log "Notarization request submitted, UUID: $REQUEST_UUID"
+
+ # Wait for notarization to complete
+ debug_log "Waiting for notarization to complete..."
+ TIMEOUT=30 # 30 minutes timeout
+ COUNT=0
+ NOTARIZATION_STATUS="in progress"
+
+ while [[ "$NOTARIZATION_STATUS" == "in progress" && $COUNT -lt $TIMEOUT ]]; do
+ sleep 60
+
+ xcrun altool --notarization-info "$REQUEST_UUID" \
+ --username "${{ inputs.apple-id }}" \
+ --password "${{ inputs.app-password }}" \
+ > "$WORK_DIR/notarization_info.txt" 2>&1
+
+ cat "$WORK_DIR/notarization_info.txt" | tee -a "$DEBUG_LOG_PATH"
+
+ NOTARIZATION_STATUS=$(grep -o "Status: .*" "$WORK_DIR/notarization_info.txt" | cut -d ':' -f2 | xargs)
+
+ debug_log "Notarization status: $NOTARIZATION_STATUS"
+ COUNT=$((COUNT+1))
+ done
+
+ if [[ "$NOTARIZATION_STATUS" == "success" ]]; then
+ debug_log "Notarization successful"
+ echo "NOTARIZATION_RESULT=true" >> $GITHUB_ENV
+ else
+ debug_log "Notarization failed or timed out: $NOTARIZATION_STATUS"
+ if [[ -f "$WORK_DIR/notarization_info.txt" ]]; then
+ cat "$WORK_DIR/notarization_info.txt"
+ fi
+ echo "NOTARIZATION_RESULT=false" >> $GITHUB_ENV
+ fi
+ else
+ debug_log "Notarization submission failed, no UUID returned"
+ cat "$WORK_DIR/notarization_output.txt"
+ echo "NOTARIZATION_RESULT=false" >> $GITHUB_ENV
+ fi
+ fi
+ shell: bash
+
+ - name: Staple notarization ticket
+ id: staple-ticket
+ if: env.SIGNING_RESULT == 'true' && env.NOTARIZATION_RESULT == 'true'
+ run: |
+ debug_log "Stapling notarization ticket to app"
+
+ # Staple the ticket
+ xcrun stapler staple "$APP_PATH"
+ STAPLE_RESULT=$?
+
+ if [ $STAPLE_RESULT -eq 0 ]; then
+ debug_log "Notarization ticket stapled successfully"
+ echo "STAPLING_RESULT=true" >> $GITHUB_ENV
+
+ # Verify stapling
+ debug_log "Verifying notarization stapling"
+ xcrun stapler validate "$APP_PATH"
+ if [ $? -eq 0 ]; then
+ debug_log "Stapling validation successful"
+ else
+ debug_log "Stapling validation failed, but continuing"
+ fi
+ else
+ debug_log "Stapling failed"
+ echo "STAPLING_RESULT=false" >> $GITHUB_ENV
+ fi
+ shell: bash
+
+ - name: Remove quarantine attribute
+ id: remove-quarantine
+ if: env.SIGNING_RESULT != 'none'
+ run: |
+ debug_log "Removing quarantine attribute from app"
+
+ # Create helper script
+ QUARANTINE_SCRIPT="$WORK_DIR/remove_quarantine.sh"
+ cat > "$QUARANTINE_SCRIPT" << 'EOF'
+#!/bin/bash
+# Removes the quarantine attribute from app and all its contents
+echo "Removing quarantine attribute from all files..."
+find "$1" -exec xattr -d com.apple.quarantine {} \; 2>/dev/null || true
+echo "Quarantine attributes removed"
+EOF
+ chmod +x "$QUARANTINE_SCRIPT"
+
+ # Remove quarantine attribute
+ "$QUARANTINE_SCRIPT" "$APP_PATH"
+
+ debug_log "Quarantine attribute removal completed"
+ shell: bash
+
+ - name: Package signed app
+ id: package-app
+ run: |
+ debug_log "Packaging the signed app"
+
+ # Check if we should use create-dmg if available
+ if command -v create-dmg &> /dev/null; then
+ debug_log "Using create-dmg for DMG creation"
+
+ # Create a temporary directory for DMG contents
+ DMG_TEMP_DIR="$WORK_DIR/dmg-contents"
+ mkdir -p "$DMG_TEMP_DIR"
+
+ # Copy the app to the temporary directory
+ cp -R "$APP_PATH" "$DMG_TEMP_DIR/"
+
+ # Create instructions text file
+ echo "Drag the application to the Applications folder to install it." > "$DMG_TEMP_DIR/README.txt"
+
+ # Create symlink to Applications folder
+ ln -s /Applications "$DMG_TEMP_DIR/Applications"
+
+ # Use create-dmg to create a more beautiful DMG
+ create-dmg \
+ --volname "$APP_NAME" \
+ --window-pos 200 120 \
+ --window-size 800 400 \
+ --icon-size 100 \
+ --app-drop-link 600 185 \
+ --icon "$APP_NAME.app" 200 185 \
+ --hide-extension "$APP_NAME.app" \
+ --add-file "README.txt" 400 185 \
+ --no-internet-enable \
+ "$DMG_PATH" \
+ "$DMG_TEMP_DIR"
+
+ DMG_CREATE_RESULT=$?
+
+ elif command -v hdiutil &> /dev/null; then
+ debug_log "Using hdiutil for DMG creation"
+
+ # Create DMG using hdiutil
+ hdiutil create -volname "$APP_NAME" -srcfolder "$APP_PATH" -ov -format UDZO "$DMG_PATH"
+ DMG_CREATE_RESULT=$?
+
+ else
+ debug_log "Neither create-dmg nor hdiutil available. Cannot create DMG."
+ DMG_CREATE_RESULT=1
+ fi
+
+ # Check DMG creation result
+ if [ $DMG_CREATE_RESULT -eq 0 ]; then
+ debug_log "DMG package created successfully at: $DMG_PATH"
+ echo "DMG_CREATED=true" >> $GITHUB_ENV
+ else
+ debug_log "DMG creation failed"
+ echo "DMG_CREATED=false" >> $GITHUB_ENV
+ fi
+
+ # If we have a properly signed app, sign the DMG as well
+ if [[ "$SIGNING_RESULT" == "true" && "$DMG_CREATED" == "true" ]]; then
+ debug_log "Signing DMG with Developer ID certificate"
+ codesign --force --timestamp --sign "$SIGNING_IDENTITY" "$DMG_PATH"
+
+ if [ $? -eq 0 ]; then
+ debug_log "DMG signed successfully"
+ else
+ debug_log "DMG signing failed"
+ fi
+
+ # If app was notarized, also notarize the DMG
+ if [[ "$NOTARIZATION_RESULT" == "true" ]]; then
+ debug_log "Notarizing DMG..."
+
+ # Notarize the DMG
+ if [[ "${{ inputs.notarization-method }}" == "api-key" ]]; then
+ debug_log "Notarizing DMG with API key method"
+
+ xcrun notarytool submit "$DMG_PATH" \
+ --key "$API_KEY_FILE" \
+ --key-id "${{ inputs.notary-api-key-id }}" \
+ --issuer "${{ inputs.notary-api-key-issuer-id }}" \
+ --wait > "$WORK_DIR/dmg_notarization_output.txt" 2>&1
+
+ cat "$WORK_DIR/dmg_notarization_output.txt" | tee -a "$DEBUG_LOG_PATH"
+
+ DMG_REQUEST_STATUS=$(grep -o "status: .*" "$WORK_DIR/dmg_notarization_output.txt" | cut -d ' ' -f2)
+
+ if [[ "$DMG_REQUEST_STATUS" == "Accepted" ]]; then
+ debug_log "DMG notarization successful"
+
+ # Staple DMG
+ debug_log "Stapling notarization ticket to DMG"
+ xcrun stapler staple "$DMG_PATH"
+ if [ $? -eq 0 ]; then
+ debug_log "DMG stapling successful"
+ else
+ debug_log "DMG stapling failed"
+ fi
+ else
+ debug_log "DMG notarization failed or timed out"
+ cat "$WORK_DIR/dmg_notarization_output.txt"
+ fi
+
+ else
+ debug_log "Notarizing DMG with app-specific password method"
+
+ xcrun altool --notarize-app \
+ --primary-bundle-id "$BUNDLE_ID.dmg" \
+ --username "${{ inputs.apple-id }}" \
+ --password "${{ inputs.app-password }}" \
+ --file "$DMG_PATH" \
+ > "$WORK_DIR/dmg_notarization_output.txt" 2>&1
+
+ cat "$WORK_DIR/dmg_notarization_output.txt" | tee -a "$DEBUG_LOG_PATH"
+
+ DMG_REQUEST_UUID=$(grep -o "RequestUUID = .*" "$WORK_DIR/dmg_notarization_output.txt" | cut -d ' ' -f3)
+
+ if [[ -n "$DMG_REQUEST_UUID" ]]; then
+ debug_log "DMG notarization request submitted, UUID: $DMG_REQUEST_UUID"
+
+ # Wait for notarization to complete
+ debug_log "Waiting for DMG notarization to complete..."
+ TIMEOUT=10 # 10 minutes timeout for DMG
+ COUNT=0
+ DMG_NOTARIZATION_STATUS="in progress"
+
+ while [[ "$DMG_NOTARIZATION_STATUS" == "in progress" && $COUNT -lt $TIMEOUT ]]; do
+ sleep 60
+
+ xcrun altool --notarization-info "$DMG_REQUEST_UUID" \
+ --username "${{ inputs.apple-id }}" \
+ --password "${{ inputs.app-password }}" \
+ > "$WORK_DIR/dmg_notarization_info.txt" 2>&1
+
+ cat "$WORK_DIR/dmg_notarization_info.txt" | tee -a "$DEBUG_LOG_PATH"
+
+ DMG_NOTARIZATION_STATUS=$(grep -o "Status: .*" "$WORK_DIR/dmg_notarization_info.txt" | cut -d ':' -f2 | xargs)
+
+ debug_log "DMG notarization status: $DMG_NOTARIZATION_STATUS"
+ COUNT=$((COUNT+1))
+ done
+
+ if [[ "$DMG_NOTARIZATION_STATUS" == "success" ]]; then
+ debug_log "DMG notarization successful"
+
+ # Staple DMG
+ debug_log "Stapling notarization ticket to DMG"
+ xcrun stapler staple "$DMG_PATH"
+ if [ $? -eq 0 ]; then
+ debug_log "DMG stapling successful"
+ else
+ debug_log "DMG stapling failed"
+ fi
+ else
+ debug_log "DMG notarization failed or timed out: $DMG_NOTARIZATION_STATUS"
+ if [[ -f "$WORK_DIR/dmg_notarization_info.txt" ]]; then
+ cat "$WORK_DIR/dmg_notarization_info.txt"
+ fi
+ fi
+ else
+ debug_log "DMG notarization submission failed, no UUID returned"
+ cat "$WORK_DIR/dmg_notarization_output.txt"
+ fi
+ fi
+ fi
+ fi
+
+ # Final verification of all distribution artifacts
+ debug_log "Verifying final distribution artifacts"
+
+ # Check ZIP file
+ if [[ -f "$ZIP_PATH" ]]; then
+ ZIP_SIZE=$(du -h "$ZIP_PATH" | cut -f1)
+ debug_log "ZIP package size: $ZIP_SIZE"
+
+ # Verify ZIP integrity
+ unzip -t "$ZIP_PATH" > /dev/null
+ if [ $? -eq 0 ]; then
+ debug_log "ZIP package integrity verified"
+ else
+ debug_log "ZIP package may be corrupted"
+ fi
+ fi
+
+ # Check DMG file
+ if [[ -f "$DMG_PATH" ]]; then
+ DMG_SIZE=$(du -h "$DMG_PATH" | cut -f1)
+ debug_log "DMG package size: $DMG_SIZE"
+
+ # Verify DMG signature if signed
+ if [[ "$SIGNING_RESULT" == "true" ]]; then
+ codesign -vvv "$DMG_PATH" 2>&1 | tee -a "$DEBUG_LOG_PATH" || debug_log "DMG signature verification failed"
+ fi
+ fi
+ shell: bash
+
+ - name: Set outputs
+ id: set-outputs
+ run: |
+ debug_log "Setting action outputs"
+
+ # Pass through environment variables to outputs
+ if [[ "$SIGNING_RESULT" == "true" ]]; then
+ echo "signed=true" >> $GITHUB_OUTPUT
+ elif [[ "$SIGNING_RESULT" == "ad-hoc" ]]; then
+ echo "signed=ad-hoc" >> $GITHUB_OUTPUT
+ else
+ echo "signed=none" >> $GITHUB_OUTPUT
+ fi
+
+ if [[ "$NOTARIZATION_RESULT" == "true" ]]; then
+ echo "notarized=true" >> $GITHUB_OUTPUT
+ else
+ echo "notarized=false" >> $GITHUB_OUTPUT
+ fi
+
+ echo "app-path=$APP_PATH" >> $GITHUB_OUTPUT
+ echo "zip-path=$ZIP_PATH" >> $GITHUB_OUTPUT
+
+ if [[ "$DMG_CREATED" == "true" ]]; then
+ echo "package-path=$DMG_PATH" >> $GITHUB_OUTPUT
+ else
+ echo "package-path=$ZIP_PATH" >> $GITHUB_OUTPUT
+ fi
+
+ debug_log "Action completed"
+ shell: bash
+
+ - name: Clean up
+ if: always()
+ run: |
+ debug_log "Cleaning up"
+
+ # Clean up keychain
+ if [[ -n "$KEYCHAIN_NAME" ]]; then
+ security delete-keychain "$KEYCHAIN_NAME" || true
+ debug_log "Keychain deleted"
+ fi
+
+ # Clean up temporary files
+ if [[ -d "$WORK_DIR" ]]; then
+ rm -rf "$WORK_DIR" || true
+ debug_log "Temporary files deleted"
+ fi
+
+ debug_log "Cleanup completed"
+ shell: bash
\ No newline at end of file
diff --git a/.gitea/actions/windows-build/action.yml b/.gitea/actions/windows-build/action.yml
new file mode 100644
index 00000000..1518429e
--- /dev/null
+++ b/.gitea/actions/windows-build/action.yml
@@ -0,0 +1,42 @@
+name: 'Windows Build Steps'
+description: 'Build Windows application'
+
+runs:
+ using: "composite"
+ steps:
+ - name: Setup environment
+ run: |
+ # Set environment variables for Unreal Engine
+ echo "UE_ROOT=E:/Games/UE_5.5" >> $GITHUB_ENV
+
+ # Create directories for builds
+ if (!(Test-Path "Builds/Windows")) { New-Item -ItemType Directory -Path "Builds/Windows" -Force }
+ if (!(Test-Path "PackagedReleases")) { New-Item -ItemType Directory -Path "PackagedReleases" -Force }
+ shell: pwsh
+
+ - name: Build for Windows
+ run: |
+ # Chmod command doesn't exist in Windows, use PowerShell to run the bash script
+ & 'C:\Program Files\Git\bin\bash.exe' -c "./scripts/win_build.sh"
+ shell: pwsh
+
+ - name: Package Windows build
+ run: |
+ echo "Packaging Windows build..."
+ if [ -d "Builds/Windows" ]; then
+ cd Builds/Windows
+ zip -r ../../PackagedReleases/LuckyRobots-Windows.zip .
+ cd ../..
+ fi
+
+ echo "=== Packaged Windows release ==="
+ ls -la PackagedReleases/
+ shell: bash
+
+ - name: Upload Windows Build Artifact
+ uses: actions/upload-artifact@v3
+ if: success() && hashFiles('PackagedReleases/LuckyRobots-Windows.zip') != ''
+ with:
+ name: LuckyRobots-Windows
+ path: PackagedReleases/LuckyRobots-Windows.zip
+ retention-days: 365
\ No newline at end of file
diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml
new file mode 100644
index 00000000..28027bfd
--- /dev/null
+++ b/.gitea/workflows/build.yml
@@ -0,0 +1,209 @@
+name: Unreal Engine Build
+
+on:
+ workflow_dispatch:
+ # push:
+ # branches: [ozgur/build]
+
+jobs:
+ windows-build:
+ runs-on: windows
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+ with:
+ lfs: true
+ fetch-depth: 0
+
+ - name: Build Windows
+ uses: ./.gitea/actions/windows-build
+
+ linux-build:
+ runs-on: windows
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+ with:
+ lfs: true
+ fetch-depth: 0
+
+ - name: Build Linux
+ uses: ./.gitea/actions/linux-build
+
+ macos-build:
+ runs-on: macos
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+ with:
+ lfs: true
+ fetch-depth: 0
+
+ - name: Build macOS
+ uses: ./.gitea/actions/macos-build
+ with:
+ apple_team_id: ${{ secrets.APPLE_TEAM_ID }}
+ apple_certificate_base64: ${{ secrets.MACOS_CERTIFICATE }}
+ apple_certificate_password: ${{ secrets.MACOS_CERTIFICATE_PWD }}
+ api_key_path: ${{ secrets.NOTARY_API_KEY_PATH }}
+ api_key_id: ${{ secrets.NOTARY_API_KEY_ID }}
+ api_key_issuer_id: ${{ secrets.NOTARY_API_KEY_ISSUER_ID }}
+
+ create-release:
+ needs: [windows-build, linux-build, macos-build]
+ runs-on: ubuntu-latest
+ if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop'
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+
+ - name: Create Tag
+ run: |
+ # Fetch all tags
+ git fetch --tags
+
+ # Get the latest version tag, if any
+ LATEST_TAG=$(git tag -l "v[0-9]*.[0-9]*.[0-9]*" | sort -V | tail -n1)
+
+ if [ -z "$LATEST_TAG" ]; then
+ # No previous version tag, start with 1.0.0
+ NEW_VERSION="1.0.0"
+ echo "No previous version tags found, starting with 1.0.0"
+ else
+ # Strip 'v' prefix if it exists
+ VERSION=${LATEST_TAG#v}
+
+ # Split version into parts
+ MAJOR=$(echo $VERSION | cut -d. -f1)
+ MINOR=$(echo $VERSION | cut -d. -f2)
+ PATCH=$(echo $VERSION | cut -d. -f3)
+
+ # Auto-increment patch version
+ PATCH=$((PATCH + 1))
+ NEW_VERSION="${MAJOR}.${MINOR}.${PATCH}"
+ echo "Auto-incremented patch version from ${VERSION} to ${NEW_VERSION}"
+ fi
+
+ # Final tag with v prefix
+ TAG="v${NEW_VERSION}"
+ echo "Creating git tag: $TAG"
+
+ # Configure git with token authentication
+ git config --global user.email "actions@gitea.com"
+ git config --global user.name "Gitea Actions"
+
+ # Direct token approach
+ git remote set-url origin "https://runner:${{ secrets.GITEATOKEN }}@luckyrobots.com/luckyrobots/luckyworld.git"
+
+ # Check if tag exists
+ if ! git rev-parse "$TAG" >/dev/null 2>&1; then
+ # Create tag
+ git tag -a "$TAG" -m "Release $TAG"
+
+ # Push tag
+ git push origin "$TAG"
+ echo "Successfully created and pushed tag: $TAG"
+ else
+ echo "Tag $TAG already exists, skipping tag creation"
+ fi
+ echo "RELEASE_TAG=$TAG" >> $GITHUB_ENV
+
+ - name: Download all artifacts
+ uses: actions/download-artifact@v3
+ with:
+ path: releases
+
+ - name: Create Build Info
+ run: |
+ # Create a build info JSON file
+ echo '{
+ "version": "${{ env.RELEASE_TAG }}",
+ "buildNumber": "${{ github.run_number }}",
+ "commit": "${{ github.sha }}",
+ "branch": "${{ github.ref_name }}",
+ "buildDate": "'$(date -u +"%Y-%m-%dT%H:%M:%SZ")'",
+ "artifacts": {
+ "windows": "https://luckyrobots.com/luckyrobots/luckyworld/actions/runs/${{ github.run_id }}/artifacts/LuckyRobots-Windows",
+ "linux": "https://luckyrobots.com/luckyrobots/luckyworld/actions/runs/${{ github.run_id }}/artifacts/LuckyRobots-Linux",
+ "macos": "https://luckyrobots.com/luckyrobots/luckyworld/actions/runs/${{ github.run_id }}/artifacts/LuckyRobots-macOS"
+ }
+ }' > build-info.json
+
+ # Create a simple HTML download page
+ echo '
+
+
+
+
+ LuckyRobots ${{ env.RELEASE_TAG }} Downloads
+
+
+
+ LuckyRobots Game - ${{ env.RELEASE_TAG }}
+ Build #${{ github.run_number }} - Built from commit: ${{ github.sha }}
+
+
+
+
+
+
+
+
+
+ ' > downloads.html
+
+ - name: Create Release
+ uses: https://gitea.com/actions/gitea-release-action@main
+ with:
+ files: |-
+ build-info.json
+ downloads.html
+ token: '${{ secrets.GITEATOKEN }}'
+ title: 'Release ${{ env.RELEASE_TAG }}'
+ body: |
+ ## LuckyRobots Game Release ${{ env.RELEASE_TAG }}
+
+ ### Download Links
+
+ Download builds from our CI artifacts:
+
+ - [Windows Build](https://luckyrobots.com/luckyrobots/luckyworld/actions/runs/${{ github.run_id }}/artifacts/LuckyRobots-Windows)
+ - [Linux Build](https://luckyrobots.com/luckyrobots/luckyworld/actions/runs/${{ github.run_id }}/artifacts/LuckyRobots-Linux)
+ - [macOS Build](https://luckyrobots.com/luckyrobots/luckyworld/actions/runs/${{ github.run_id }}/artifacts/LuckyRobots-macOS)
+
+ ### Build Information
+
+ - Build Number: #${{ github.run_number }}
+ - Commit: ${{ github.sha }}
+ - Branch: ${{ github.ref_name }}
+ - Build Date: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
+ prerelease: ${{ github.ref != 'refs/heads/main' }}
+ tag_name: '${{ env.RELEASE_TAG }}'
+
\ No newline at end of file
diff --git a/.gitea/workflows/create-release.yml b/.gitea/workflows/create-release.yml
deleted file mode 100644
index 555b5af2..00000000
--- a/.gitea/workflows/create-release.yml
+++ /dev/null
@@ -1,103 +0,0 @@
-name: Unreal Release
-
-on:
- workflow_dispatch:
- inputs:
- windows_build_path:
- description: 'Absolute path to the Windows build zip file'
- required: true
- default: 'E:\LuckyWorld\LuckyRobots\UNREAL_PROJECTS\Luckyrobots\Builds\Windows\LuckyRobots-Windows.zip'
- linux_build_path:
- description: 'Absolute path to the Linux build zip file'
- required: true
- default: 'E:\LuckyWorld\LuckyRobots\UNREAL_PROJECTS\Luckyrobots\Builds\Linux\LuckyRobots-Linux.zip'
- mac_build_path:
- description: 'Absolute path to the Mac build zip file'
- required: true
- default: 'E:\LuckyWorld\LuckyRobots\UNREAL_PROJECTS\Luckyrobots\Builds\Mac\LuckyRobots-Mac.zip'
-
-jobs:
- build:
- runs-on: windows
- steps:
- - name: Upload Linux Build Artifact
- uses: actions/upload-artifact@v3
- with:
- name: LuckyRobots-Linux
- path: ${{ github.event.inputs.linux_build_path }}
- retention-days: 365
-
- - name: Upload Windows Build Artifact
- uses: actions/upload-artifact@v3
- with:
- name: LuckyRobots-Windows
- path: ${{ github.event.inputs.windows_build_path }}
- retention-days: 365
-
- - name: Upload Mac Build Artifact
- uses: actions/upload-artifact@v3
- with:
- name: LuckyRobots-Mac
- path: ${{ github.event.inputs.mac_build_path }}
- retention-days: 365
-
- - name: Get Release Tag
- shell: pwsh
- run: |
- # Fetch all tags
- git fetch --tags
-
- # Get the latest version tag, if any
- # Uses Sort-Object with a version comparison scriptblock
- $latestTag = git tag -l "v[0-9]*.[0-9]*.[0-9]*" | Sort-Object -Property @{Expression={[version]($_ -replace 'v')}} | Select-Object -Last 1
-
- $newVersion = "1.0.0" # Default start version
-
- if ($null -ne $latestTag -and $latestTag -ne '') {
- Write-Host "Latest tag found: $latestTag"
- # Strip 'v' prefix
- $versionString = $latestTag -replace '^v'
-
- # Split version into parts
- $versionParts = $versionString.Split('.')
- if ($versionParts.Length -eq 3) {
- $major = [int]$versionParts[0]
- $minor = [int]$versionParts[1]
- $patch = [int]$versionParts[2]
-
- # Auto-increment patch version
- $patch++
- $newVersion = "$major.$minor.$patch"
- Write-Host "Auto-incremented patch version from $versionString to $newVersion"
- } else {
- Write-Host "Could not parse version from tag: $latestTag. Defaulting to 1.0.0"
- }
- } else {
- Write-Host "No previous version tags found, starting with 1.0.0"
- }
-
- # Final tag with v prefix
- $tag = "v$newVersion"
-
- # Set environment variable for subsequent steps
- echo "RELEASE_TAG=$tag" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
- Write-Host "Using release tag: $tag"
-
- - name: Create Release
- uses: https://gitea.com/actions/gitea-release-action@main
- with:
- token: '${{ secrets.GITEA_TOKEN }}'
- title: 'Release ${{ env.RELEASE_TAG }}'
- body: |
- ## LuckyRobots Game Release ${{ env.RELEASE_TAG }}
-
- Windows, Linux and Mac builds are attached below.
-
- ### Build Information
-
- - Build Number: #${{ github.run_number }}
- - Commit: ${{ github.sha }}
- - Branch: ${{ github.ref_name }}
- - Build Date: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
- prerelease: ${{ github.ref != 'refs/heads/main' }}
- tag_name: '${{ env.RELEASE_TAG }}'
\ No newline at end of file
diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml
new file mode 100644
index 00000000..76e687ff
--- /dev/null
+++ b/.gitea/workflows/release.yml
@@ -0,0 +1,163 @@
+name: Create Release
+
+on:
+ workflow_dispatch:
+ workflow_call:
+
+jobs:
+ create-release:
+ runs-on: ubuntu-latest
+ if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop'
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+
+ - name: Create Tag
+ run: |
+ # Fetch all tags
+ git fetch --tags
+
+ # Get the latest version tag, if any
+ LATEST_TAG=$(git tag -l "v[0-9]*.[0-9]*.[0-9]*" | sort -V | tail -n1)
+
+ if [ -z "$LATEST_TAG" ]; then
+ # No previous version tag, start with 1.0.0
+ NEW_VERSION="1.0.0"
+ echo "No previous version tags found, starting with 1.0.0"
+ else
+ # Strip 'v' prefix if it exists
+ VERSION=${LATEST_TAG#v}
+
+ # Split version into parts
+ MAJOR=$(echo $VERSION | cut -d. -f1)
+ MINOR=$(echo $VERSION | cut -d. -f2)
+ PATCH=$(echo $VERSION | cut -d. -f3)
+
+ # Auto-increment patch version
+ PATCH=$((PATCH + 1))
+ NEW_VERSION="${MAJOR}.${MINOR}.${PATCH}"
+ echo "Auto-incremented patch version from ${VERSION} to ${NEW_VERSION}"
+ fi
+
+ # Final tag with v prefix
+ TAG="v${NEW_VERSION}"
+ echo "Creating git tag: $TAG"
+
+ # Configure git with token authentication
+ git config --global user.email "actions@gitea.com"
+ git config --global user.name "Gitea Actions"
+
+ # Direct token approach
+ git remote set-url origin "https://runner:${{ secrets.GITEATOKEN }}@luckyrobots.com/luckyrobots/luckyworld.git"
+
+ # Check if tag exists
+ if ! git rev-parse "$TAG" >/dev/null 2>&1; then
+ # Create tag
+ git tag -a "$TAG" -m "Release $TAG"
+
+ # Push tag
+ git push origin "$TAG"
+ echo "Successfully created and pushed tag: $TAG"
+ else
+ echo "Tag $TAG already exists, skipping tag creation"
+ fi
+ echo "RELEASE_TAG=$TAG" >> $GITHUB_ENV
+
+ - name: Download all artifacts
+ uses: actions/download-artifact@v3
+ with:
+ path: releases
+
+ - name: Create Build Info
+ run: |
+ # Create a build info JSON file
+ echo '{
+ "version": "${{ env.RELEASE_TAG }}",
+ "buildNumber": "${{ github.run_number }}",
+ "commit": "${{ github.sha }}",
+ "branch": "${{ github.ref_name }}",
+ "buildDate": "'$(date -u +"%Y-%m-%dT%H:%M:%SZ")'",
+ "artifacts": {
+ "windows": "https://luckyrobots.com/luckyrobots/luckyworld/actions/runs/${{ github.run_id }}/artifacts/LuckyRobots-Windows",
+ "linux": "https://luckyrobots.com/luckyrobots/luckyworld/actions/runs/${{ github.run_id }}/artifacts/LuckyRobots-Linux",
+ "macos": "https://luckyrobots.com/luckyrobots/luckyworld/actions/runs/${{ github.run_id }}/artifacts/LuckyRobots-macOS"
+ }
+ }' > build-info.json
+
+ # Create a simple HTML download page
+ echo '
+
+
+
+
+ LuckyRobots ${{ env.RELEASE_TAG }} Downloads
+
+
+
+ LuckyRobots Game - ${{ env.RELEASE_TAG }}
+ Build #${{ github.run_number }} - Built from commit: ${{ github.sha }}
+
+
+
+
+
+
+
+
+
+ ' > downloads.html
+
+ - name: Create Release
+ uses: https://gitea.com/actions/gitea-release-action@main
+ with:
+ files: |-
+ build-info.json
+ downloads.html
+ token: '${{ secrets.GITEATOKEN }}'
+ title: 'Release ${{ env.RELEASE_TAG }}'
+ body: |
+ ## LuckyRobots Game Release ${{ env.RELEASE_TAG }}
+
+ ### Download Links
+
+ Download builds from our CI artifacts:
+
+ - [Windows Build](https://luckyrobots.com/luckyrobots/luckyworld/actions/runs/${{ github.run_id }}/artifacts/LuckyRobots-Windows)
+ - [Linux Build](https://luckyrobots.com/luckyrobots/luckyworld/actions/runs/${{ github.run_id }}/artifacts/LuckyRobots-Linux)
+ - [macOS Build](https://luckyrobots.com/luckyrobots/luckyworld/actions/runs/${{ github.run_id }}/artifacts/LuckyRobots-macOS)
+
+ ### Build Information
+
+ - Build Number: #${{ github.run_number }}
+ - Commit: ${{ github.sha }}
+ - Branch: ${{ github.ref_name }}
+ - Build Date: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
+ prerelease: ${{ github.ref != 'refs/heads/main' }}
+ tag_name: '${{ env.RELEASE_TAG }}'
\ No newline at end of file
diff --git a/.gitea/workflows/test-local-signing.yml b/.gitea/workflows/test-local-signing.yml
new file mode 100644
index 00000000..5f13a79e
--- /dev/null
+++ b/.gitea/workflows/test-local-signing.yml
@@ -0,0 +1,392 @@
+name: Test Local Signing
+
+on:
+ workflow_dispatch: # Manual trigger
+ # push:
+ # branches: [ozgur/build]
+
+jobs:
+ test-local-signing:
+ runs-on: macos
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+
+ - name: Create Test Entitlements
+ run: |
+ echo "๐ Creating entitlements file..."
+ cat > LuckyWorld.entitlements << EOF
+
+
+
+
+ com.apple.security.cs.allow-jit
+
+ com.apple.security.cs.allow-unsigned-executable-memory
+
+ com.apple.security.cs.disable-library-validation
+
+ com.apple.security.cs.allow-dyld-environment-variables
+
+ com.apple.security.device.audio-input
+
+ com.apple.security.device.camera
+
+ com.apple.security.automation.apple-events
+
+ com.apple.security.get-task-allow
+
+
+
+ EOF
+
+ echo "โ
Created entitlements file"
+ cat LuckyWorld.entitlements
+ shell: bash
+
+ - name: Create Test App Bundle
+ run: |
+ echo "๐ฆ Creating test app bundle..."
+
+ # Create test app bundle structure
+ TEST_APP_DIR="TestApp.app"
+ mkdir -p "$TEST_APP_DIR/Contents/MacOS"
+
+ # Create a simple test executable
+ echo '#!/bin/bash
+ echo "Hello from TestApp!"' > "$TEST_APP_DIR/Contents/MacOS/TestApp"
+ chmod +x "$TEST_APP_DIR/Contents/MacOS/TestApp"
+
+ # Create Info.plist
+ cat > "$TEST_APP_DIR/Contents/Info.plist" << EOF
+
+
+
+
+ CFBundleExecutable
+ TestApp
+ CFBundleIdentifier
+ com.luckyrobots.luckyworld.testapp
+ CFBundleName
+ TestApp
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.0
+ LSMinimumSystemVersion
+ 10.10
+
+
+ EOF
+
+ echo "โ
Created test app bundle"
+
+ # Verify app bundle exists
+ if [ ! -d "$TEST_APP_DIR" ]; then
+ echo "โ Error: App bundle not found at $TEST_APP_DIR"
+ exit 1
+ fi
+
+ echo "๐ App bundle contents:"
+ ls -la "$TEST_APP_DIR"
+
+ # Store app path as environment variable
+ echo "APP_PATH=$(pwd)/TestApp.app" >> "$GITHUB_ENV"
+ shell: bash
+
+ - name: Setup Certificate
+ env:
+ CERTIFICATE_BASE64: ${{ secrets.MACOS_CERTIFICATE }}
+ CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PWD }}
+ APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
+ run: |
+ echo "๐ Setting up certificate..."
+
+ # Create a temporary directory for certificates
+ CERT_DIR="$HOME/certificates"
+ mkdir -p "$CERT_DIR"
+
+ # Decode the certificate to a p12 file
+ echo "$CERTIFICATE_BASE64" | base64 --decode > "$CERT_DIR/certificate.p12"
+
+ # Check certificate format and details
+ echo "๐ Certificate format check:"
+ file "$CERT_DIR/certificate.p12"
+
+ # Try to get certificate info with openssl
+ echo "๐ Certificate info with OpenSSL:"
+ openssl pkcs12 -info -in "$CERT_DIR/certificate.p12" -nokeys -passin pass:"$CERTIFICATE_PASSWORD" || echo "Failed to read certificate with OpenSSL"
+
+ # Create keychain
+ KEYCHAIN_PATH="$CERT_DIR/app-signing.keychain-db"
+ KEYCHAIN_PASSWORD="temppassword123"
+
+ # Delete existing keychain if it exists
+ security delete-keychain "$KEYCHAIN_PATH" 2>/dev/null || true
+
+ # Create new keychain
+ security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
+ security set-keychain-settings -t 3600 -u -l "$KEYCHAIN_PATH"
+ security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
+
+ # Add to search list and make default
+ security list-keychains -d user -s "$KEYCHAIN_PATH" $(security list-keychains -d user | tr -d '"')
+ security default-keychain -s "$KEYCHAIN_PATH"
+
+ # Try multiple import approaches
+ echo "๐ Importing developer certificate - attempt 1 (standard)..."
+ security import "$CERT_DIR/certificate.p12" -k "$KEYCHAIN_PATH" -P "$CERTIFICATE_PASSWORD" -T /usr/bin/codesign
+
+ echo "๐ Importing developer certificate - attempt 2 (with flags)..."
+ security import "$CERT_DIR/certificate.p12" -k "$KEYCHAIN_PATH" -P "$CERTIFICATE_PASSWORD" -T /usr/bin/codesign -x -A
+
+ echo "๐ Importing developer certificate - attempt 3 (with format)..."
+ security import "$CERT_DIR/certificate.p12" -k "$KEYCHAIN_PATH" -P "$CERTIFICATE_PASSWORD" -T /usr/bin/codesign -f pkcs12
+
+ # Set partition list for codesign to access keychain
+ security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
+
+ # Check all certificates in keychain
+ echo "๐ Listing all certificates in keychain..."
+ security find-certificate -a "$KEYCHAIN_PATH"
+
+ # Verify certificate
+ echo "๐ Verifying code signing identities..."
+ security find-identity -v -p codesigning "$KEYCHAIN_PATH"
+
+ # Alternative check for identities
+ echo "๐ Listing identities with code signing usage..."
+ security find-certificate -a -c "Developer ID Application" -p "$KEYCHAIN_PATH" | grep -q "Code Signing" && echo "โ
Certificate has code signing usage" || echo "โ Certificate does NOT have code signing usage"
+
+ # Try to use the System keychain as a fallback
+ echo "๐ Checking system keychain for code signing identities..."
+ SYSTEM_IDENTITIES=$(security find-identity -v -p codesigning)
+ echo "$SYSTEM_IDENTITIES"
+
+ if echo "$SYSTEM_IDENTITIES" | grep -q "Developer ID Application"; then
+ echo "โ
Found Developer ID Application certificate in system keychain"
+ echo "USE_SYSTEM_CERT=true" >> "$GITHUB_ENV"
+ else
+ echo "โ No Developer ID Application certificate found in system keychain"
+ echo "USE_SYSTEM_CERT=false" >> "$GITHUB_ENV"
+ fi
+
+ # Store keychain variables for later steps
+ echo "KEYCHAIN_PATH=$KEYCHAIN_PATH" >> "$GITHUB_ENV"
+ echo "KEYCHAIN_PASSWORD=$KEYCHAIN_PASSWORD" >> "$GITHUB_ENV"
+ echo "APPLE_TEAM_ID=$APPLE_TEAM_ID" >> "$GITHUB_ENV"
+
+ # Debug: keep p12 file for inspection
+ echo "๐พ Keeping certificate.p12 for debugging"
+ shell: bash
+
+ - name: Debug Certificate Content
+ if: always()
+ env:
+ CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PWD }}
+ run: |
+ echo "๐ Debugging certificate content..."
+ CERT_DIR="$HOME/certificates"
+
+ # Check if p12 file exists
+ if [ ! -f "$CERT_DIR/certificate.p12" ]; then
+ echo "โ Certificate file not found"
+ exit 0
+ fi
+
+ # Try with OpenSSL to extract certificate info
+ echo "Attempting to extract certificate info..."
+ openssl pkcs12 -in "$CERT_DIR/certificate.p12" -info -nokeys -passin pass:"$CERTIFICATE_PASSWORD" > cert_info.txt || echo "Failed to extract info"
+
+ # Check certificate contents
+ echo "Certificate subject information:"
+ grep "subject" cert_info.txt || echo "No subject information found"
+
+ echo "Certificate issuer information:"
+ grep "issuer" cert_info.txt || echo "No issuer information found"
+
+ # Check if it's a Developer ID certificate
+ if grep -q "Developer ID" cert_info.txt; then
+ echo "โ
This appears to be a Developer ID certificate"
+ else
+ echo "โ This does NOT appear to be a Developer ID certificate"
+ fi
+
+ # Check if it has a private key
+ echo "Checking for private key..."
+ if openssl pkcs12 -in "$CERT_DIR/certificate.p12" -nocerts -passin pass:"$CERTIFICATE_PASSWORD" -passout pass:temp 2>/dev/null; then
+ echo "โ
Certificate contains a private key"
+ else
+ echo "โ Certificate does NOT contain a private key or wrong password"
+ fi
+ shell: bash
+
+ - name: Sign with Developer ID
+ run: |
+ echo "๐ Signing app with Developer ID certificate..."
+
+ # Decide which keychain to use
+ if [ "${USE_SYSTEM_CERT:-false}" = "true" ]; then
+ echo "Using system keychain identity"
+ # Get certificate hash instead of name to avoid ambiguity
+ IDENTITY_HASH=$(security find-identity -v -p codesigning | grep "Developer ID Application" | head -1 | awk '{print $2}')
+ echo "Using certificate hash: $IDENTITY_HASH"
+ else
+ # Make sure keychain is unlocked
+ security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
+ echo "Using custom keychain identity"
+ # Get certificate hash instead of name to avoid ambiguity
+ IDENTITY_HASH=$(security find-identity -v -p codesigning "$KEYCHAIN_PATH" | grep "Developer ID Application" | head -1 | awk '{print $2}')
+ echo "Using certificate hash: $IDENTITY_HASH"
+ fi
+
+ if [ -z "$IDENTITY_HASH" ]; then
+ echo "โ No valid Developer ID Application certificate found"
+ echo "Falling back to ad-hoc signing for testing..."
+ # Use ad-hoc identity as fallback
+ codesign --force --deep --verbose --options runtime --entitlements LuckyWorld.entitlements --sign - --timestamp "$APP_PATH"
+ echo "SIGNED=adhoc" >> "$GITHUB_ENV"
+ else
+ echo "Signing app bundle with Developer ID hash: $IDENTITY_HASH"
+
+ # Sign the app bundle using the hash
+ codesign --force --deep --verbose --options runtime --entitlements LuckyWorld.entitlements --sign "$IDENTITY_HASH" --timestamp "$APP_PATH"
+ echo "SIGNED=identity" >> "$GITHUB_ENV"
+ fi
+
+ # Verify signing
+ echo "๐ Verifying signature..."
+ codesign -vvv --deep --strict "$APP_PATH"
+
+ # Check entitlements
+ echo "๐ Checking entitlements..."
+ codesign -d --entitlements - "$APP_PATH"
+ shell: bash
+
+ - name: Notarize App
+ if: success()
+ env:
+ APPLE_ID: ${{ secrets.NOTARY_USER }}
+ APP_PASSWORD: ${{ secrets.NOTARY_PASSWORD }}
+ API_KEY_ID: ${{ secrets.NOTARY_API_KEY_ID }}
+ API_ISSUER_ID: ${{ secrets.NOTARY_API_KEY_ISSUER_ID }}
+ API_KEY_PATH: ${{ secrets.NOTARY_API_KEY_PATH }}
+ run: |
+ echo "๐ค Notarizing app..."
+
+ # Check if we have API key credentials
+ if [ -n "$API_KEY_ID" ] && [ -n "$API_ISSUER_ID" ] && [ -n "$API_KEY_PATH" ]; then
+ echo "Using App Store Connect API key for notarization..."
+
+ # Create directory for API key if API_KEY_PATH contains content
+ mkdir -p ~/private_keys
+
+ # Check if API_KEY_PATH is a path or content
+ if [[ "$API_KEY_PATH" == /* ]] && [ -f "$API_KEY_PATH" ]; then
+ # It's a path to a file
+ echo "Using API key from path: $API_KEY_PATH"
+ cp "$API_KEY_PATH" ~/private_keys/AuthKey_${API_KEY_ID}.p8
+ else
+ # It contains the key content
+ echo "Using API key from content"
+ echo "$API_KEY_PATH" > ~/private_keys/AuthKey_${API_KEY_ID}.p8
+ fi
+
+ # Create zip for notarization
+ ZIP_PATH="TestApp-notarize.zip"
+ ditto -c -k --keepParent "$APP_PATH" "$ZIP_PATH"
+
+ echo "Submitting for notarization with API key..."
+ xcrun notarytool submit "$ZIP_PATH" \
+ --key ~/private_keys/AuthKey_${API_KEY_ID}.p8 \
+ --key-id "$API_KEY_ID" \
+ --issuer "$API_ISSUER_ID" \
+ --wait
+
+ # Staple the notarization ticket
+ echo "Stapling notarization ticket..."
+ xcrun stapler staple "$APP_PATH"
+
+ # Verify notarization
+ echo "๐ Verifying notarization..."
+ spctl --assess --verbose --type exec "$APP_PATH"
+
+ echo "NOTARIZED=true" >> "$GITHUB_ENV"
+
+ # Clean up
+ rm -rf ~/private_keys
+
+ # Fall back to App-specific password if API key not available
+ elif [ -n "$APPLE_ID" ] && [ -n "$APP_PASSWORD" ] && [ -n "$APPLE_TEAM_ID" ]; then
+ echo "Using App-specific password for notarization..."
+
+ # Create zip for notarization
+ ZIP_PATH="TestApp-notarize.zip"
+ ditto -c -k --keepParent "$APP_PATH" "$ZIP_PATH"
+
+ echo "Submitting for notarization..."
+ xcrun notarytool submit "$ZIP_PATH" \
+ --apple-id "$APPLE_ID" \
+ --password "$APP_PASSWORD" \
+ --team-id "$APPLE_TEAM_ID" \
+ --wait
+
+ # Staple the notarization ticket
+ echo "Stapling notarization ticket..."
+ xcrun stapler staple "$APP_PATH"
+
+ # Verify notarization
+ echo "๐ Verifying notarization..."
+ spctl --assess --verbose --type exec "$APP_PATH"
+
+ echo "NOTARIZED=true" >> "$GITHUB_ENV"
+ else
+ echo "โ ๏ธ Missing notarization credentials. Skipping notarization."
+ echo "For App Store Connect API key method, set these secrets:"
+ echo " - NOTARY_API_KEY_ID: Your API key ID"
+ echo " - NOTARY_API_KEY_ISSUER_ID: Your API issuer ID"
+ echo " - NOTARY_API_KEY_PATH: Path to or content of your p8 file"
+ echo ""
+ echo "For App-specific password method, set these secrets:"
+ echo " - NOTARY_USER: Your Apple ID (email)"
+ echo " - NOTARY_PASSWORD: Your app-specific password"
+ echo " - APPLE_TEAM_ID: Your Apple Developer team ID"
+
+ echo "NOTARIZED=false" >> "$GITHUB_ENV"
+ exit 0
+ fi
+ shell: bash
+
+ - name: Package Signed App
+ run: |
+ echo "๐ฆ Packaging signed app..."
+
+ if [ "${NOTARIZED:-false}" == "true" ]; then
+ ZIP_FILE="TestApp-Signed-Notarized.zip"
+ echo "Creating distribution package with notarized app..."
+ else
+ ZIP_FILE="TestApp-Signed.zip"
+ echo "Creating distribution package with signed app..."
+ fi
+
+ # Create zip package
+ ditto -c -k --keepParent "$APP_PATH" "$ZIP_FILE"
+
+ echo "โ
Created package: $ZIP_FILE"
+ shell: bash
+
+ - name: Upload Artifact
+ uses: actions/upload-artifact@v3
+ with:
+ name: LuckyWorld-Signed-App
+ path: TestApp-*.zip
+ retention-days: 7
+
+ - name: Cleanup
+ if: always()
+ run: |
+ echo "๐งน Cleaning up..."
+ rm -rf TestApp.app TestApp-*.zip || true
+ security delete-keychain "$KEYCHAIN_PATH" 2>/dev/null || true
+ echo "โ
Cleanup complete"
+ shell: bash
\ No newline at end of file
diff --git a/.gitea/workflows/test-macos-build.yml b/.gitea/workflows/test-macos-build.yml
new file mode 100644
index 00000000..6380acd3
--- /dev/null
+++ b/.gitea/workflows/test-macos-build.yml
@@ -0,0 +1,709 @@
+name: Test macOS Build Action
+
+on:
+ workflow_dispatch: # Manual trigger only for testing
+ push:
+ branches: [ozgur/build]
+
+jobs:
+ test-macos-build:
+ runs-on: macos
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+ with:
+ lfs: true
+ fetch-depth: 0
+
+ # Enable debug logging
+ - name: Enable Debug Logging
+ run: |
+ echo "ACTIONS_RUNNER_DEBUG=true" >> $GITHUB_ENV
+ echo "ACTIONS_STEP_DEBUG=true" >> $GITHUB_ENV
+ shell: bash
+
+ # Setup environment for build
+ - name: Setup environment
+ run: |
+ # Get the working directory path for absolute paths
+ WORKSPACE_DIR="$(pwd)"
+ echo "WORKSPACE_DIR=$WORKSPACE_DIR" >> "$GITHUB_ENV"
+ echo "ENTITLEMENTS_FILE=LuckyWorld.entitlements" >> "$GITHUB_ENV"
+
+ # Set CI environment variable to true for build script
+ echo "CI=true" >> "$GITHUB_ENV"
+
+ # Create directories for builds
+ mkdir -p Builds/Mac
+ mkdir -p PackagedReleases
+ mkdir -p ArchivedApps
+
+ echo "Environment setup complete"
+ shell: bash
+
+ # Restore cache for build dependencies
+ - name: Restore Build Cache
+ id: build-cache
+ uses: actions/cache@v3
+ with:
+ path: |
+ DerivedDataCache
+ Intermediate
+ Saved/Autosaves
+ Saved/Config
+ .unreal
+ key: ${{ runner.os }}-macbuild-${{ hashFiles('**/*.uproject') }}-${{ hashFiles('Config/**') }}
+ restore-keys: |
+ ${{ runner.os }}-macbuild-${{ hashFiles('**/*.uproject') }}-
+ ${{ runner.os }}-macbuild-
+
+ # Build for macOS - use your own build script
+ - name: Build for macOS
+ run: |
+ if [ -f "./scripts/mac_build.sh" ]; then
+ chmod +x ./scripts/mac_build.sh
+ # Set CI environment variable explicitly before running
+ export CI=true
+ ./scripts/mac_build.sh
+
+ # Check if the build succeeded by looking for the app
+ APP_PATHS=$(find ./Builds -type d -name "*.app" 2>/dev/null || echo "")
+ if [ -z "$APP_PATHS" ]; then
+ APP_PATHS=$(find ./Saved/StagedBuilds -type d -name "*.app" 2>/dev/null || echo "")
+ fi
+
+ if [ -z "$APP_PATHS" ]; then
+ echo "โ ERROR: Build command appeared to succeed but no app bundle was found!"
+ echo "This usually means the build failed but didn't properly return an error code."
+ exit 1
+ fi
+ else
+ echo "ERROR: Build script not found at ./scripts/mac_build.sh"
+ exit 1
+ fi
+ shell: bash
+
+ # Find the app bundle
+ - name: Find app bundle
+ run: |
+ # Add error handling
+ set +e # Don't exit immediately on error for this block
+
+ echo "Build status check..."
+ if [ ! -d "./Builds" ] && [ ! -d "./Saved/StagedBuilds" ]; then
+ echo "โ ERROR: Build directories do not exist. Build likely failed."
+ exit 1
+ fi
+
+ # First check Saved/StagedBuilds directory - where Unreal often places built apps
+ echo "Checking Saved/StagedBuilds directory..."
+ APP_PATHS=$(find ./Saved/StagedBuilds -type d -name "*.app" 2>/dev/null || echo "")
+
+ # If not found, check Builds directory
+ if [ -z "$APP_PATHS" ]; then
+ echo "No app found in Saved/StagedBuilds, checking Builds directory..."
+ APP_PATHS=$(find ./Builds -type d -name "*.app" 2>/dev/null || echo "")
+ fi
+
+ # If still not found, check the whole workspace
+ if [ -z "$APP_PATHS" ]; then
+ echo "No app found in Builds, checking entire workspace..."
+ APP_PATHS=$(find . -type d -name "*.app" -not -path "*/\.*" 2>/dev/null || echo "")
+ fi
+
+ if [ -z "$APP_PATHS" ]; then
+ echo "โ ERROR: Could not find any app bundles!"
+ echo "Listing all directories to help debug:"
+ find . -type d -maxdepth 3 | sort
+ exit 1
+ fi
+
+ echo "Found potential app bundles:"
+ echo "$APP_PATHS"
+
+ # Use the first app path found (preferably the main app, not a child app)
+ MAIN_APP_PATH=$(echo "$APP_PATHS" | grep -v "CrashReportClient" | head -1 || echo "$APP_PATHS" | head -1)
+
+ echo "Using app bundle: $MAIN_APP_PATH"
+ echo "APP_PATH=$MAIN_APP_PATH" >> "$GITHUB_ENV"
+
+ # Make sure app exists - using local variable
+ if [ ! -d "$MAIN_APP_PATH" ]; then
+ echo "โ ERROR: App bundle not found at $MAIN_APP_PATH!"
+ exit 1
+ fi
+
+ # Export APP_PATH for next steps to use
+ echo "APP_PATH=$MAIN_APP_PATH" >> "$GITHUB_ENV"
+
+ # Get bundle ID from Info.plist for reference (not modifying)
+ if [ -f "$MAIN_APP_PATH/Contents/Info.plist" ]; then
+ BUNDLE_ID=$(/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "$MAIN_APP_PATH/Contents/Info.plist")
+ echo "Detected bundle ID: $BUNDLE_ID"
+ echo "BUNDLE_ID=$BUNDLE_ID" >> "$GITHUB_ENV"
+ fi
+ shell: bash
+
+ # Basic pre-notarization checks
+ - name: Check for notarization issues
+ run: |
+ echo "๐ Checking app for potential notarization issues..."
+
+ APP_PATH="${{ env.APP_PATH }}"
+
+ # Verify code signature already exists (from Unreal build)
+ echo "Checking existing signature..."
+ codesign -vvv "$APP_PATH" || echo "โ ๏ธ App may not be properly signed by Unreal Engine"
+
+ # Check for any ad-hoc signatures that would cause issues
+ if codesign -dvv "$APP_PATH" 2>&1 | grep -q "adhoc"; then
+ echo "โ ๏ธ Warning: Ad-hoc signature detected. This will be replaced with a proper signature."
+ fi
+
+ # Verify entitlements file exists
+ if [ ! -f "${{ env.ENTITLEMENTS_FILE }}" ]; then
+ echo "โ ๏ธ Entitlements file not found. Will use default entitlements."
+ else
+ echo "Found entitlements file: ${{ env.ENTITLEMENTS_FILE }}"
+ fi
+ shell: bash
+
+ # Save cache for next workflow run
+ - name: Save Build Cache
+ if: always()
+ uses: actions/cache/save@v3
+ with:
+ path: |
+ DerivedDataCache
+ Intermediate
+ Saved/Autosaves
+ Saved/Config
+ .unreal
+ key: ${{ steps.build-cache.outputs.cache-primary-key }}
+
+ # Create a debug log file for notarize action
+ - name: Create debug log directory
+ run: |
+ mkdir -p debug_logs
+ echo "DEBUG_LOG_PATH=$(pwd)/debug_logs/notarize_log.txt" >> $GITHUB_ENV
+ shell: bash
+
+ # Beginning of macos-notarize steps
+ - name: Setup debug environment
+ id: setup-debug-env
+ run: |
+ # Create debug directory if env variable is set
+ if [[ -n "$DEBUG_LOG_PATH" ]]; then
+ mkdir -p "$(dirname "$DEBUG_LOG_PATH")"
+ touch "$DEBUG_LOG_PATH"
+ echo "Debug logging enabled to: $DEBUG_LOG_PATH" | tee -a "$DEBUG_LOG_PATH"
+ fi
+
+ # Define a debug function
+ debug_log() {
+ echo "DEBUG: $1"
+ if [[ -n "$DEBUG_LOG_PATH" ]]; then
+ echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$DEBUG_LOG_PATH"
+ fi
+ }
+ # Export the function for use in subsequent steps
+ export -f debug_log
+
+ debug_log "Starting macOS notarize action"
+ debug_log "App path: ${{ env.APP_PATH }}"
+ debug_log "Team ID: ${{ secrets.APPLE_TEAM_ID }}"
+ debug_log "Notarization method: api-key"
+ debug_log "Bundle ID: ${{ env.BUNDLE_ID }}"
+ shell: bash
+
+ - name: Set up variables
+ id: setup
+ run: |
+ # Debugging info
+ debug_log "Setting up variables"
+
+ # Generate unique name for keychain
+ KEYCHAIN_NAME="build-keychain-$(uuidgen)"
+ KEYCHAIN_PASSWORD="$(uuidgen)"
+ echo "KEYCHAIN_NAME=$KEYCHAIN_NAME" >> $GITHUB_ENV
+ echo "KEYCHAIN_PASSWORD=$KEYCHAIN_PASSWORD" >> $GITHUB_ENV
+
+ # Set paths
+ echo "APP_PATH=${{ env.APP_PATH }}" >> $GITHUB_ENV
+
+ # Generate working directory for temp files
+ WORK_DIR="$(mktemp -d)"
+ echo "WORK_DIR=$WORK_DIR" >> $GITHUB_ENV
+
+ # Set bundle id (from input or extract from app)
+ if [[ -n "${{ env.BUNDLE_ID }}" ]]; then
+ BUNDLE_ID="${{ env.BUNDLE_ID }}"
+ else
+ BUNDLE_ID=$(/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "${{ env.APP_PATH }}/Contents/Info.plist")
+ fi
+ echo "BUNDLE_ID=$BUNDLE_ID" >> $GITHUB_ENV
+
+ # Get app name from bundle path
+ APP_NAME=$(basename "${{ env.APP_PATH }}" .app)
+ echo "APP_NAME=$APP_NAME" >> $GITHUB_ENV
+
+ # Set output directory
+ OUTPUT_DIR="$(pwd)/PackagedReleases"
+ mkdir -p "$OUTPUT_DIR"
+ echo "OUTPUT_DIR=$OUTPUT_DIR" >> $GITHUB_ENV
+
+ # Set package paths
+ ZIP_PATH="$OUTPUT_DIR/${APP_NAME}.zip"
+ DMG_PATH="$OUTPUT_DIR/${APP_NAME}.dmg"
+ echo "ZIP_PATH=$ZIP_PATH" >> $GITHUB_ENV
+ echo "DMG_PATH=$DMG_PATH" >> $GITHUB_ENV
+
+ # Set notarization variables based on method
+ echo "Using API key method for notarization"
+
+ # Create API key file
+ API_KEY_FILE="$WORK_DIR/api_key.p8"
+ echo "${{ secrets.NOTARY_API_KEY_PATH }}" | base64 --decode > "$API_KEY_FILE"
+ echo "API_KEY_FILE=$API_KEY_FILE" >> $GITHUB_ENV
+
+ # Verify API key file exists
+ if [[ ! -f "$API_KEY_FILE" ]]; then
+ debug_log "ERROR: API key file could not be created"
+ exit 1
+ fi
+
+ debug_log "API key file created at: $API_KEY_FILE"
+ debug_log "API key ID: ${{ secrets.NOTARY_API_KEY_ID }}"
+ debug_log "API key issuer ID: ${{ secrets.NOTARY_API_KEY_ISSUER_ID }}"
+ shell: bash
+
+ - name: Setup keychain
+ id: setup-keychain
+ run: |
+ debug_log "Setting up keychain"
+
+ # Create temporary keychain
+ security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
+ security default-keychain -s "$KEYCHAIN_NAME"
+ security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
+ security set-keychain-settings -t 3600 -u "$KEYCHAIN_NAME"
+
+ # Create certificate file
+ CERTIFICATE_PATH="$WORK_DIR/certificate.p12"
+ echo "${{ secrets.MACOS_CERTIFICATE }}" | base64 --decode > "$CERTIFICATE_PATH"
+
+ # Add to keychain
+ debug_log "Importing certificate into keychain"
+ security import "$CERTIFICATE_PATH" -k "$KEYCHAIN_NAME" -P "${{ secrets.MACOS_CERTIFICATE_PWD }}" -T /usr/bin/codesign
+
+ # Allow codesign to access keychain items
+ security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
+
+ # Verify certificate was imported
+ security find-identity -v "$KEYCHAIN_NAME" | grep "Developer ID Application"
+ IDENTITY_RESULT=$?
+
+ if [ $IDENTITY_RESULT -eq 0 ]; then
+ debug_log "Certificate imported successfully"
+ SIGNING_IDENTITY="Developer ID Application: ${{ secrets.APPLE_TEAM_ID }}"
+ echo "SIGNING_IDENTITY=$SIGNING_IDENTITY" >> $GITHUB_ENV
+ echo "CERTIFICATE_AVAILABLE=true" >> $GITHUB_ENV
+ else
+ debug_log "WARNING: No Developer ID Application certificate found"
+ if [[ "false" == "true" ]]; then
+ debug_log "Falling back to ad-hoc signing"
+ echo "CERTIFICATE_AVAILABLE=adhoc" >> $GITHUB_ENV
+ else
+ debug_log "Not falling back to ad-hoc signing as specified"
+ echo "CERTIFICATE_AVAILABLE=false" >> $GITHUB_ENV
+ fi
+ fi
+ shell: bash
+
+ - name: Sign application
+ id: sign-app
+ run: |
+ debug_log "Starting application signing process"
+
+ # Check if certificate is available
+ if [[ "$CERTIFICATE_AVAILABLE" == "false" ]]; then
+ debug_log "No certificate available and fallback disabled. Skipping signing."
+ echo "SIGNING_RESULT=none" >> $GITHUB_ENV
+ exit 0
+ fi
+
+ # Make sure entitlements file is defined and reset variable name
+ ENTITLEMENTS_PATH="${{ env.ENTITLEMENTS_FILE }}"
+
+ # Sign the app
+ if [[ "$CERTIFICATE_AVAILABLE" == "true" ]]; then
+ debug_log "Signing with Developer ID certificate"
+
+ # First remove existing signatures
+ debug_log "Removing existing signatures..."
+ codesign --remove-signature "$APP_PATH" || true
+
+ # Sign all dynamic libraries and frameworks
+ debug_log "Signing embedded binaries and frameworks..."
+ find "$APP_PATH/Contents/MacOS" -type f -name "*.dylib" -exec codesign --force --timestamp --options runtime --entitlements "$ENTITLEMENTS_PATH" --sign "$SIGNING_IDENTITY" {} \;
+ find "$APP_PATH/Contents/Frameworks" -type f -depth 1 -exec codesign --force --timestamp --options runtime --entitlements "$ENTITLEMENTS_PATH" --sign "$SIGNING_IDENTITY" {} \;
+ find "$APP_PATH/Contents/Frameworks" -name "*.framework" -exec codesign --force --timestamp --options runtime --entitlements "$ENTITLEMENTS_PATH" --sign "$SIGNING_IDENTITY" {} \;
+
+ # Sign all executables
+ debug_log "Signing executables..."
+ find "$APP_PATH/Contents/MacOS" -type f -exec codesign --force --timestamp --options runtime --entitlements "$ENTITLEMENTS_PATH" --sign "$SIGNING_IDENTITY" {} \;
+
+ # Sign app bundle
+ debug_log "Signing main app bundle..."
+ codesign --force --timestamp --options runtime --entitlements "$ENTITLEMENTS_PATH" --sign "$SIGNING_IDENTITY" "$APP_PATH"
+
+ SIGN_RESULT=$?
+ if [ $SIGN_RESULT -eq 0 ]; then
+ debug_log "App signed successfully with Developer ID"
+ echo "SIGNING_RESULT=true" >> $GITHUB_ENV
+ else
+ debug_log "App signing failed with Developer ID"
+ echo "SIGNING_RESULT=false" >> $GITHUB_ENV
+ exit 1
+ fi
+
+ elif [[ "$CERTIFICATE_AVAILABLE" == "adhoc" ]]; then
+ debug_log "Signing with ad-hoc identity (not suitable for distribution)"
+
+ # Remove existing signatures
+ codesign --remove-signature "$APP_PATH" || true
+
+ # Sign with ad-hoc identity
+ codesign --force --timestamp --options runtime --entitlements "$ENTITLEMENTS_PATH" --sign - "$APP_PATH"
+
+ SIGN_RESULT=$?
+ if [ $SIGN_RESULT -eq 0 ]; then
+ debug_log "App signed successfully with ad-hoc identity"
+ echo "SIGNING_RESULT=ad-hoc" >> $GITHUB_ENV
+ else
+ debug_log "App signing failed with ad-hoc identity"
+ echo "SIGNING_RESULT=false" >> $GITHUB_ENV
+ exit 1
+ fi
+ else
+ debug_log "Unexpected certificate state. Skipping signing."
+ echo "SIGNING_RESULT=none" >> $GITHUB_ENV
+ fi
+
+ # Verify signing
+ debug_log "Verifying app signature..."
+ codesign -dvv "$APP_PATH"
+ shell: bash
+
+ - name: Verify notarization and stapling
+ id: verify-notarization
+ if: env.SIGNING_RESULT == 'true'
+ run: |
+ debug_log "Verifying app signature and code requirements before notarization"
+
+ # Verify code signature
+ codesign --verify --verbose "$APP_PATH"
+ if [ $? -ne 0 ]; then
+ debug_log "Error: App signature verification failed"
+ # Don't exit, just log the error
+ else
+ debug_log "App signature verification passed"
+ fi
+
+ # Check app for code requirements
+ codesign --display --requirements "$APP_PATH"
+ if [ $? -ne 0 ]; then
+ debug_log "Error: App doesn't meet requirements"
+ # Don't exit, just log the error
+ else
+ debug_log "App meets code requirements"
+ fi
+ shell: bash
+
+ - name: Notarize application
+ id: notarize-app
+ if: env.SIGNING_RESULT == 'true'
+ run: |
+ debug_log "Starting notarization process"
+
+ # Create ZIP for notarization
+ debug_log "Creating ZIP archive for notarization"
+ ditto -c -k --keepParent "$APP_PATH" "$ZIP_PATH"
+ if [ $? -ne 0 ]; then
+ debug_log "Error creating ZIP archive"
+ echo "NOTARIZATION_RESULT=false" >> $GITHUB_ENV
+ exit 1
+ fi
+
+ # Notarize the app using API key method
+ debug_log "Notarizing with API key method"
+
+ # Submit for notarization
+ debug_log "Submitting app for notarization..."
+ xcrun notarytool submit "$ZIP_PATH" \
+ --key "$API_KEY_FILE" \
+ --key-id "${{ secrets.NOTARY_API_KEY_ID }}" \
+ --issuer "${{ secrets.NOTARY_API_KEY_ISSUER_ID }}" \
+ --wait > "$WORK_DIR/notarization_output.txt" 2>&1
+
+ cat "$WORK_DIR/notarization_output.txt" | tee -a "$DEBUG_LOG_PATH"
+
+ REQUEST_STATUS=$(grep -o "status: .*" "$WORK_DIR/notarization_output.txt" | cut -d ' ' -f2)
+
+ if [[ "$REQUEST_STATUS" == "Accepted" ]]; then
+ debug_log "Notarization successful"
+ echo "NOTARIZATION_RESULT=true" >> $GITHUB_ENV
+ else
+ debug_log "Notarization failed or timed out"
+ cat "$WORK_DIR/notarization_output.txt"
+ echo "NOTARIZATION_RESULT=false" >> $GITHUB_ENV
+ fi
+ shell: bash
+
+ - name: Staple notarization ticket
+ id: staple-ticket
+ if: env.SIGNING_RESULT == 'true' && env.NOTARIZATION_RESULT == 'true'
+ run: |
+ debug_log "Stapling notarization ticket to app"
+
+ # Staple the ticket
+ xcrun stapler staple "$APP_PATH"
+ STAPLE_RESULT=$?
+
+ if [ $STAPLE_RESULT -eq 0 ]; then
+ debug_log "Notarization ticket stapled successfully"
+ echo "STAPLING_RESULT=true" >> $GITHUB_ENV
+
+ # Verify stapling
+ debug_log "Verifying notarization stapling"
+ xcrun stapler validate "$APP_PATH"
+ if [ $? -eq 0 ]; then
+ debug_log "Stapling validation successful"
+ else
+ debug_log "Stapling validation failed, but continuing"
+ fi
+ else
+ debug_log "Stapling failed"
+ echo "STAPLING_RESULT=false" >> $GITHUB_ENV
+ fi
+ shell: bash
+
+ - name: Remove quarantine attribute
+ id: remove-quarantine
+ if: env.SIGNING_RESULT != 'none'
+ run: |
+ debug_log "Removing quarantine attribute from app"
+
+ # Directly run the command without creating a script file
+ debug_log "Removing quarantine attribute from all files..."
+ find "$APP_PATH" -exec xattr -d com.apple.quarantine {} \; 2>/dev/null || true
+ debug_log "Quarantine attributes removed"
+ shell: bash
+
+ - name: Package signed app
+ id: package-app
+ run: |
+ debug_log "Packaging the signed app"
+
+ # Check if we should use create-dmg if available
+ if command -v create-dmg &> /dev/null; then
+ debug_log "Using create-dmg for DMG creation"
+
+ # Create a temporary directory for DMG contents
+ DMG_TEMP_DIR="$WORK_DIR/dmg-contents"
+ mkdir -p "$DMG_TEMP_DIR"
+
+ # Copy the app to the temporary directory
+ cp -R "$APP_PATH" "$DMG_TEMP_DIR/"
+
+ # Create instructions text file
+ echo "Drag the application to the Applications folder to install it." > "$DMG_TEMP_DIR/README.txt"
+
+ # Create symlink to Applications folder
+ ln -s /Applications "$DMG_TEMP_DIR/Applications"
+
+ # Use create-dmg to create a more beautiful DMG
+ create-dmg \
+ --volname "$APP_NAME" \
+ --window-pos 200 120 \
+ --window-size 800 400 \
+ --icon-size 100 \
+ --app-drop-link 600 185 \
+ --icon "$APP_NAME.app" 200 185 \
+ --hide-extension "$APP_NAME.app" \
+ --add-file "README.txt" 400 185 \
+ --no-internet-enable \
+ "$DMG_PATH" \
+ "$DMG_TEMP_DIR"
+
+ DMG_CREATE_RESULT=$?
+
+ elif command -v hdiutil &> /dev/null; then
+ debug_log "Using hdiutil for DMG creation"
+
+ # Create DMG using hdiutil
+ hdiutil create -volname "$APP_NAME" -srcfolder "$APP_PATH" -ov -format UDZO "$DMG_PATH"
+ DMG_CREATE_RESULT=$?
+
+ else
+ debug_log "Neither create-dmg nor hdiutil available. Cannot create DMG."
+ DMG_CREATE_RESULT=1
+ fi
+
+ # Check DMG creation result
+ if [ $DMG_CREATE_RESULT -eq 0 ]; then
+ debug_log "DMG package created successfully at: $DMG_PATH"
+ echo "DMG_CREATED=true" >> $GITHUB_ENV
+ else
+ debug_log "DMG creation failed"
+ echo "DMG_CREATED=false" >> $GITHUB_ENV
+ fi
+
+ # If we have a properly signed app, sign the DMG as well
+ if [[ "$SIGNING_RESULT" == "true" && "$DMG_CREATED" == "true" ]]; then
+ debug_log "Signing DMG with Developer ID certificate"
+ codesign --force --timestamp --sign "$SIGNING_IDENTITY" "$DMG_PATH"
+
+ if [ $? -eq 0 ]; then
+ debug_log "DMG signed successfully"
+ else
+ debug_log "DMG signing failed"
+ fi
+
+ # If app was notarized, also notarize the DMG
+ if [[ "$NOTARIZATION_RESULT" == "true" ]]; then
+ debug_log "Notarizing DMG..."
+
+ # Notarize the DMG using API key method
+ debug_log "Notarizing DMG with API key method"
+
+ xcrun notarytool submit "$DMG_PATH" \
+ --key "$API_KEY_FILE" \
+ --key-id "${{ secrets.NOTARY_API_KEY_ID }}" \
+ --issuer "${{ secrets.NOTARY_API_KEY_ISSUER_ID }}" \
+ --wait > "$WORK_DIR/dmg_notarization_output.txt" 2>&1
+
+ cat "$WORK_DIR/dmg_notarization_output.txt" | tee -a "$DEBUG_LOG_PATH"
+
+ DMG_REQUEST_STATUS=$(grep -o "status: .*" "$WORK_DIR/dmg_notarization_output.txt" | cut -d ' ' -f2)
+
+ if [[ "$DMG_REQUEST_STATUS" == "Accepted" ]]; then
+ debug_log "DMG notarization successful"
+
+ # Staple DMG
+ debug_log "Stapling notarization ticket to DMG"
+ xcrun stapler staple "$DMG_PATH"
+ if [ $? -eq 0 ]; then
+ debug_log "DMG stapling successful"
+ else
+ debug_log "DMG stapling failed"
+ fi
+ else
+ debug_log "DMG notarization failed or timed out"
+ cat "$WORK_DIR/dmg_notarization_output.txt"
+ fi
+ fi
+ fi
+
+ # Final verification of all distribution artifacts
+ debug_log "Verifying final distribution artifacts"
+
+ # Check ZIP file
+ if [[ -f "$ZIP_PATH" ]]; then
+ ZIP_SIZE=$(du -h "$ZIP_PATH" | cut -f1)
+ debug_log "ZIP package size: $ZIP_SIZE"
+
+ # Verify ZIP integrity
+ unzip -t "$ZIP_PATH" > /dev/null
+ if [ $? -eq 0 ]; then
+ debug_log "ZIP package integrity verified"
+ else
+ debug_log "ZIP package may be corrupted"
+ fi
+ fi
+
+ # Check DMG file
+ if [[ -f "$DMG_PATH" ]]; then
+ DMG_SIZE=$(du -h "$DMG_PATH" | cut -f1)
+ debug_log "DMG package size: $DMG_SIZE"
+
+ # Verify DMG signature if signed
+ if [[ "$SIGNING_RESULT" == "true" ]]; then
+ codesign -vvv "$DMG_PATH" 2>&1 | tee -a "$DEBUG_LOG_PATH" || debug_log "DMG signature verification failed"
+ fi
+ fi
+
+ # Set output variables
+ if [[ "$SIGNING_RESULT" == "true" ]]; then
+ echo "SIGNED_STATUS=true" >> $GITHUB_ENV
+ elif [[ "$SIGNING_RESULT" == "ad-hoc" ]]; then
+ echo "SIGNED_STATUS=ad-hoc" >> $GITHUB_ENV
+ else
+ echo "SIGNED_STATUS=none" >> $GITHUB_ENV
+ fi
+
+ if [[ "$NOTARIZATION_RESULT" == "true" ]]; then
+ echo "NOTARIZED_STATUS=true" >> $GITHUB_ENV
+ else
+ echo "NOTARIZED_STATUS=false" >> $GITHUB_ENV
+ fi
+
+ if [[ "$DMG_CREATED" == "true" ]]; then
+ echo "PACKAGE_PATH=$DMG_PATH" >> $GITHUB_ENV
+ else
+ echo "PACKAGE_PATH=$ZIP_PATH" >> $GITHUB_ENV
+ fi
+ shell: bash
+
+ - name: Clean up
+ if: always()
+ run: |
+ debug_log "Cleaning up"
+
+ # Clean up keychain
+ if [[ -n "$KEYCHAIN_NAME" ]]; then
+ security delete-keychain "$KEYCHAIN_NAME" || true
+ debug_log "Keychain deleted"
+ fi
+
+ # Clean up temporary files
+ if [[ -d "$WORK_DIR" ]]; then
+ rm -rf "$WORK_DIR" || true
+ debug_log "Temporary files deleted"
+ fi
+
+ debug_log "Cleanup completed"
+ shell: bash
+
+ # Upload debug logs if available
+ - name: Upload Debug Logs
+ uses: actions/upload-artifact@v3
+ if: always()
+ with:
+ name: notarize-debug-logs
+ path: debug_logs
+ retention-days: 7
+
+ # Upload only the DMG file as main distribution artifact
+ - name: Upload Mac Distribution DMG
+ uses: actions/upload-artifact@v3
+ if: env.NOTARIZED_STATUS == 'true' && env.SIGNED_STATUS != 'none'
+ with:
+ name: LuckyWorld-Mac-Distribution
+ path: ${{ env.PACKAGE_PATH }}
+ retention-days: 30
+
+ # Report results
+ - name: Report Results
+ run: |
+ echo "๐ App signing: ${{ env.SIGNED_STATUS }}"
+ echo "๐ App notarization: ${{ env.NOTARIZED_STATUS }}"
+
+ if [ "${{ env.SIGNED_STATUS }}" != "none" ]; then
+ echo "โ
Packaging completed successfully!"
+ echo "Final package: ${{ env.PACKAGE_PATH }}"
+ else
+ echo "โ ๏ธ App was not signed - check the logs for details"
+ fi
+ shell: bash
+
\ No newline at end of file
diff --git a/.gitea/workflows/unreal-build.yml b/.gitea/workflows/unreal-build.yml
deleted file mode 100644
index b106bcdd..00000000
--- a/.gitea/workflows/unreal-build.yml
+++ /dev/null
@@ -1,347 +0,0 @@
-name: Unreal Engine Build
-
-on:
- workflow_dispatch:
-
-jobs:
- build-and-release:
- runs-on: windows
- if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop'
- steps:
- - name: Checkout repository
- uses: actions/checkout@v3
- with:
- lfs: true
- fetch-depth: 0
-
- - name: Setup environment
- run: |
- # Set environment variables for Unreal Engine
- echo "UE_ROOT=E:/Games/UE_5.5" >> $GITHUB_ENV
- # Set environment variables for Linux toolchain
- $env:LINUX_MULTIARCH_ROOT="C:/UnrealToolchains/v23_clang-18.1.0-rockylinux8"
- echo "LINUX_MULTIARCH_ROOT=${LINUX_MULTIARCH_ROOT}" >> $GITHUB_ENV
-
- # Create directories for builds (with error handling)
- if (!(Test-Path "Builds/Windows")) { New-Item -ItemType Directory -Path "Builds/Windows" -Force }
- if (!(Test-Path "Builds/Linux")) { New-Item -ItemType Directory -Path "Builds/Linux" -Force }
- if (!(Test-Path "PackagedReleases")) { New-Item -ItemType Directory -Path "PackagedReleases" -Force }
-
- - name: Build for Windows
- run: |
- # Chmod command doesn't exist in Windows, use PowerShell to run the bash script
- & 'C:\Program Files\Git\bin\bash.exe' -c "./win_build.sh"
-
- - name: Build for Linux
- run: |
- # Chmod command doesn't exist in Windows, use PowerShell to run the bash script
- & 'C:\Program Files\Git\bin\bash.exe' -c "./linux_build.sh"
-
- - name: Package builds
- run: |
- echo "Packaging Windows build..."
- if [ -d "Builds/Windows" ]; then
- cd Builds/Windows
- zip -r ../../PackagedReleases/LuckyRobots-Windows.zip .
- cd ../..
- fi
-
- echo "Packaging Linux build..."
- if [ -d "Builds/Linux" ]; then
- cd Builds/Linux
- zip -r ../../PackagedReleases/LuckyRobots-Linux.zip .
- cd ../..
- fi
-
- echo "=== Packaged releases ==="
- ls -la PackagedReleases/
-
- - name: Upload Windows Build Artifact
- uses: actions/upload-artifact@v3
- if: success() && hashFiles('PackagedReleases/LuckyRobots-Windows.zip') != ''
- with:
- name: LuckyRobots-Windows
- path: PackagedReleases/LuckyRobots-Windows.zip
- retention-days: 365
-
- - name: Upload Linux Build Artifact
- uses: actions/upload-artifact@v3
- if: success() && hashFiles('PackagedReleases/LuckyRobots-Linux.zip') != ''
- with:
- name: LuckyRobots-Linux
- path: PackagedReleases/LuckyRobots-Linux.zip
- retention-days: 365
-
- - name: Create Tag
- if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop'
- run: |
- # Fetch all tags
- git fetch --tags
-
- # Get the latest version tag, if any
- LATEST_TAG=$(git tag -l "v[0-9]*.[0-9]*.[0-9]*" | sort -V | tail -n1)
-
- if [ -z "$LATEST_TAG" ]; then
- # No previous version tag, start with 1.0.0
- NEW_VERSION="1.0.0"
- echo "No previous version tags found, starting with 1.0.0"
- else
- # Strip 'v' prefix if it exists
- VERSION=${LATEST_TAG#v}
-
- # Split version into parts
- MAJOR=$(echo $VERSION | cut -d. -f1)
- MINOR=$(echo $VERSION | cut -d. -f2)
- PATCH=$(echo $VERSION | cut -d. -f3)
-
- # Auto-increment patch version
- PATCH=$((PATCH + 1))
- NEW_VERSION="${MAJOR}.${MINOR}.${PATCH}"
- echo "Auto-incremented patch version from ${VERSION} to ${NEW_VERSION}"
- fi
-
- # Final tag with v prefix
- TAG="v${NEW_VERSION}"
- echo "Creating git tag: $TAG"
-
- # Configure git with token authentication
- git config --global user.email "actions@gitea.com"
- git config --global user.name "Gitea Actions"
-
- # Direct token approach - simplest method
- git remote set-url origin "https://goran:${{ secrets.GITEATOKEN }}@luckyrobots.com/luckyrobots/luckyworld.git"
-
- # Set git to not prompt for input
- $env:GIT_TERMINAL_PROMPT=0
-
- # Check if tag exists
- if ! git rev-parse "$TAG" >/dev/null 2>&1; then
- # Create tag without opening editor (-m flag)
- git tag -a "$TAG" -m "Release $TAG"
-
- # Push with timeout and debug
- echo "Pushing tag $TAG to origin..."
- git push --verbose origin "$TAG" || {
- echo "Error: Failed to push tag. Check your token permissions."
- exit 1
- }
- echo "Successfully created and pushed tag: $TAG"
- else
- echo "Tag $TAG already exists, skipping tag creation"
- fi
- echo "RELEASE_TAG=$TAG" >> $GITHUB_ENV
-
- - name: Create Build Info
- run: |
- # Create a build info JSON file
- echo '{
- "version": "${{ env.RELEASE_TAG }}",
- "buildNumber": "${{ github.run_number }}",
- "commit": "${{ github.sha }}",
- "branch": "${{ github.ref_name }}",
- "buildDate": "'$(date -u +"%Y-%m-%dT%H:%M:%SZ")'",
- "artifacts": {
- "windows": "https://luckyrobots.com/luckyrobots/luckyworld/actions/runs/${{ github.run_id }}/artifacts/LuckyRobots-Windows",
- "linux": "https://luckyrobots.com/luckyrobots/luckyworld/actions/runs/${{ github.run_id }}/artifacts/LuckyRobots-Linux"
- }
- }' > PackagedReleases/build-info.json
-
- # Create a simple HTML download page
- echo '
-
-
-
-
- LuckyRobots ${{ env.RELEASE_TAG }} Downloads
-
-
-
- LuckyRobots Game - ${{ env.RELEASE_TAG }}
- Build #${{ github.run_number }} - Built from commit: ${{ github.sha }}
-
-
-
-
-
-
-
- ' > PackagedReleases/downloads.html
-
- - name: Create Release
- uses: https://gitea.com/actions/gitea-release-action@main
- with:
- files: |-
- PackagedReleases/build-info.json
- PackagedReleases/downloads.html
- token: '${{ secrets.GITEA_TOKEN }}'
- title: 'Release ${{ env.RELEASE_TAG }}'
- body: |
- ## LuckyRobots Game Release ${{ env.RELEASE_TAG }}
-
- ### Download Links
-
- Download builds from our CI artifacts:
-
- - [Windows Build](https://luckyrobots.com/luckyrobots/luckyworld/actions/runs/${{ github.run_id }}/artifacts/LuckyRobots-Windows)
- - [Linux Build](https://luckyrobots.com/luckyrobots/luckyworld/actions/runs/${{ github.run_id }}/artifacts/LuckyRobots-Linux)
-
- ### Build Information
-
- - Build Number: #${{ github.run_number }}
- - Commit: ${{ github.sha }}
- - Branch: ${{ github.ref_name }}
- - Build Date: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
- prerelease: ${{ github.ref != 'refs/heads/main' }}
- tag_name: '${{ env.RELEASE_TAG }}'
-
- macos-build:
- runs-on: macos
- if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop'
- steps:
- - name: Checkout repository
- uses: actions/checkout@v3
- with:
- lfs: true
- fetch-depth: 0
-
- - name: Get Release Tag
- run: |
- # Fetch all tags
- git fetch --tags
-
- # Get the latest version tag
- LATEST_TAG=$(git tag -l "v[0-9]*.[0-9]*.[0-9]*" | sort -V | tail -n1)
-
- if [ -z "$LATEST_TAG" ]; then
- NEW_VERSION="1.0.0"
- else
- VERSION=${LATEST_TAG#v}
- MAJOR=$(echo $VERSION | cut -d. -f1)
- MINOR=$(echo $VERSION | cut -d. -f2)
- PATCH=$(echo $VERSION | cut -d. -f3)
- PATCH=$((PATCH + 1))
- NEW_VERSION="${MAJOR}.${MINOR}.${PATCH}"
- fi
-
- TAG="v${NEW_VERSION}"
- echo "RELEASE_TAG=$TAG" >> $GITHUB_ENV
- echo "Using release tag: $TAG"
-
- - name: Setup Unreal Engine
- run: |
- # Use the correct path where Unreal Engine is installed
- UE_PATH="/Users/Shared/Epic Games/UE_5.5"
-
- if [ ! -d "$UE_PATH" ]; then
- echo "Error: Unreal Engine is not installed in the expected location"
- echo "Please ensure Unreal Engine is installed at $UE_PATH"
- exit 1
- fi
-
- # Set environment variable with the correct Engine path
- echo "UE_ROOT=$UE_PATH/Engine" >> $GITHUB_ENV
- echo "Using Unreal Engine 5.5"
-
- - name: Build Unreal Project
- run: |
- chmod +x ./mac_build.sh
- ./mac_build.sh
-
- - name: Prepare Mac release
- run: |
- echo "Preparing packaged files for release..."
-
- # Create a directory for release files
- mkdir -p PackagedReleases
-
- # Debug: Show what we're packaging
- echo "=== Packaging for Release ==="
- echo "Build directory contents:"
- ls -la Builds/
-
- # Find the app bundle in the Builds directory
- APP_PATH=$(find Builds -type d -name "*.app" | head -1)
-
- if [ -n "$APP_PATH" ]; then
- echo "Found app bundle: $APP_PATH"
- # Get the app name
- APP_NAME=$(basename "$APP_PATH")
- # Create zip file of the app bundle
- (cd $(dirname "$APP_PATH") && zip -r "../../PackagedReleases/${APP_NAME%.app}-macOS.zip" "$APP_NAME")
- echo "Created packaged release: PackagedReleases/${APP_NAME%.app}-macOS.zip"
- else
- echo "No .app bundle found in Builds directory"
-
- # Look for a directory that might be a bundle but not named .app
- MAIN_BUILD_DIR=$(find Builds -mindepth 1 -maxdepth 1 -type d | head -1)
- if [ -n "$MAIN_BUILD_DIR" ]; then
- echo "Found main build directory: $MAIN_BUILD_DIR"
- DIR_NAME=$(basename "$MAIN_BUILD_DIR")
- # Package this directory as if it were the app
- (cd $(dirname "$MAIN_BUILD_DIR") && zip -r "../../PackagedReleases/${DIR_NAME}-macOS.zip" "$DIR_NAME")
- echo "Created packaged release from main directory: PackagedReleases/${DIR_NAME}-macOS.zip"
- else
- # Package the entire Builds directory as a fallback
- echo "No main directory found, packaging everything"
- zip -r "PackagedReleases/LuckyRobots-macOS.zip" Builds
- echo "Created fallback package: PackagedReleases/LuckyRobots-macOS.zip"
- fi
- fi
-
- echo "Packaged releases:"
- ls -la PackagedReleases/
-
- - name: Upload macOS Build Artifact
- uses: actions/upload-artifact@v3
- if: success()
- with:
- name: LuckyRobots-macOS
- path: PackagedReleases/*-macOS.zip
- retention-days: 365
-
- - name: Create Release Note
- run: |
- echo "## macOS Build Completed" > release-note.md
- echo "" >> release-note.md
- echo "macOS build is available as an artifact." >> release-note.md
- echo "" >> release-note.md
- echo "Download from: [macOS Build](https://luckyrobots.com/luckyrobots/luckyworld/actions/runs/${{ github.run_id }}/artifacts/LuckyRobots-macOS)" >> release-note.md
-
- - name: Create Gitea Release
- if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop'
- uses: https://gitea.com/actions/gitea-release-action@main
- with:
- token: ${{ secrets.GITEATOKEN }}
- tag_name: ${{ env.RELEASE_TAG }}
- title: "Release ${{ env.RELEASE_TAG }} - macOS"
- body: |
- ## macOS Build Available as Artifact
-
- The macOS build is available as an artifact due to its large file size.
-
- [Download macOS Build](https://luckyrobots.com/luckyrobots/luckyworld/actions/runs/${{ github.run_id }}/artifacts/LuckyRobots-macOS)
-
- Built from commit: ${{ github.sha }}
- files: release-note.md
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 21abe81c..dceaf989 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,3 +41,4 @@ DerivedDataCache/*
#this only is the Binaries folder on the root, not the Binaries folder in the plugin folders
Binaries/**
*.app/
+.cursorrules
diff --git a/Config/DefaultEngine.ini b/Config/DefaultEngine.ini
index 761b6da7..e8b910a9 100644
--- a/Config/DefaultEngine.ini
+++ b/Config/DefaultEngine.ini
@@ -338,3 +338,35 @@ NearClipPlane=0.100000
bFinalUsesRDO=True
FinalRDOLambda=100
+[/Script/MacTargetPlatform.MacTargetSettings]
+TargetedRHIs=SF_METAL_SM5
+MetalLanguageVersion=5
+MaxShaderLanguageVersion=4
+MinimumOSVersion=11
+BundleName=LuckyWorld
+BundleDisplayName=LuckyWorld
+bEnableMathOptimizations=True
+UseFastIntrinsics=True
+EnableMipGenOption=Default
+FrameRateLock=PUFRL_None
+AudioSampleRate=48000
+AudioMaxChannels=32
+bUseCustomIcon=False
+bUseMiniUPnP=False
+MetalDynamicLibraries=()
+MetalRuntimeLibrary=1
+OutputRealFPS=False
+bBuildEmbeddedFrameworksForGame=False
+EnableCodeCoverage=False
+EnableCodeCoveragePath=(Path="")
+ForwardShading=False
+UseFastCopyToResolve=True
+bAutomaticallySignBuilds=False
+bUseSIPSafeRunloop=True
+CodeSigningIdentity=""
+
+[/Script/MacTargetPlatform.XcodeProjectSettings]
+CodeSigningPrefix=com.luckyrobots
+ApplicationDisplayName=LuckyWorld
+ShippingSpecificMacEntitlements=(FilePath="../LuckyWorld.entitlements")
+
diff --git a/Config/DefaultGame.ini b/Config/DefaultGame.ini
index 96915495..7cae8a2f 100644
--- a/Config/DefaultGame.ini
+++ b/Config/DefaultGame.ini
@@ -7,6 +7,9 @@ ProjectVersion=0.1
;bAddPacks=True
;InsertPack=(PackSource="StarterContent.upack",PackName="StarterContent")
+[/Script/MacTargetPlatform.MacTargetSettings]
+BundleIdentifier=com.luckyrobots.luckyworld
+
[/Script/UnrealEd.ProjectPackagingSettings]
Build=IfProjectHasCode
BuildConfiguration=PPBC_Shipping
diff --git a/LuckyWorld.entitlements b/LuckyWorld.entitlements
new file mode 100644
index 00000000..d8b1cb47
--- /dev/null
+++ b/LuckyWorld.entitlements
@@ -0,0 +1,30 @@
+
+
+
+
+ com.apple.security.cs.allow-jit
+
+ com.apple.security.cs.allow-unsigned-executable-memory
+
+ com.apple.security.cs.disable-library-validation
+
+ com.apple.security.cs.allow-dyld-environment-variables
+
+ com.apple.security.device.audio-input
+
+ com.apple.security.device.camera
+
+ com.apple.security.automation.apple-events
+
+ com.apple.security.cs.debugger
+
+ com.apple.security.files.user-selected.read-write
+
+ com.apple.security.network.client
+
+ com.apple.security.automation.apple-events
+
+ com.apple.security.get-task-allow
+
+
+
\ No newline at end of file
diff --git a/Source/LuckyWorld.Target.cs b/Source/LuckyWorld.Target.cs
index 25223105..86aedaa3 100644
--- a/Source/LuckyWorld.Target.cs
+++ b/Source/LuckyWorld.Target.cs
@@ -9,7 +9,7 @@ public class LuckyWorldTarget : TargetRules
{
Type = TargetType.Game;
DefaultBuildSettings = BuildSettingsVersion.V5;
-
+
ExtraModuleNames.AddRange( new string[] { "LuckyWorld" } );
IncludeOrderVersion = EngineIncludeOrderVersion.Latest;
diff --git a/Source/LuckyWorld/LuckyWorld.Build.cs b/Source/LuckyWorld/LuckyWorld.Build.cs
index d6c887a7..9d80c354 100644
--- a/Source/LuckyWorld/LuckyWorld.Build.cs
+++ b/Source/LuckyWorld/LuckyWorld.Build.cs
@@ -19,5 +19,6 @@ public class LuckyWorld : ModuleRules
// PrivateDependencyModuleNames.Add("OnlineSubsystem");
// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
+
}
}
diff --git a/Source/LuckyWorldEditor.Target.cs b/Source/LuckyWorldEditor.Target.cs
index a698b47d..b1d60a9d 100644
--- a/Source/LuckyWorldEditor.Target.cs
+++ b/Source/LuckyWorldEditor.Target.cs
@@ -9,7 +9,7 @@ public class LuckyWorldEditorTarget : TargetRules
{
Type = TargetType.Editor;
DefaultBuildSettings = BuildSettingsVersion.V5;
-
+
ExtraModuleNames.AddRange( new string[] { "LuckyWorld" } );
IncludeOrderVersion = EngineIncludeOrderVersion.Latest;
diff --git a/mac_build.sh b/mac_build.sh
deleted file mode 100755
index fe68fdfa..00000000
--- a/mac_build.sh
+++ /dev/null
@@ -1,55 +0,0 @@
-#!/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"
-
-rm -rf DerivedDataCache Intermediate Binaries Saved
-
-"$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)
\ No newline at end of file
diff --git a/linux_build.sh b/scripts/linux_build.sh
similarity index 100%
rename from linux_build.sh
rename to scripts/linux_build.sh
diff --git a/scripts/mac_build.sh b/scripts/mac_build.sh
new file mode 100755
index 00000000..d07c69ab
--- /dev/null
+++ b/scripts/mac_build.sh
@@ -0,0 +1,131 @@
+#!/bin/bash
+set -e # Exit on any error
+
+# 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 entitlements file
+if [ -f "$PROJECT_ROOT/LuckyWorld.entitlements" ]; then
+ ENTITLEMENTS_FILE="$PROJECT_ROOT/LuckyWorld.entitlements"
+ echo "โ
Using entitlements file: $ENTITLEMENTS_FILE"
+else
+ echo "โ ๏ธ Warning: No entitlements file found. This might affect notarization."
+ ENTITLEMENTS_FILE=""
+fi
+
+# Print paths and config for debugging
+echo "Project root: $PROJECT_ROOT"
+echo "Project file: $PROJECT_FILE"
+echo "Archive directory: $ARCHIVE_DIR"
+
+# More selective cleanup - don't remove DerivedDataCache
+echo "๐งน Cleaning build artifacts..."
+rm -rf DerivedDataCache Intermediate Binaries Saved
+mkdir -p "$ARCHIVE_DIR"
+
+# Generate project files
+echo "๐ Generating project files..."
+"$UE_ROOT/Engine/Build/BatchFiles/Mac/GenerateProjectFiles.sh" -project="$PROJECT_FILE" -game -engine
+
+# Run the build command with simplified parameters and more diagnostics
+echo "๐จ Starting build process..."
+"$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)
+ #!/bin/bash
+
+# Check for errors in the build process
+BUILD_STATUS=$?
+if [ $BUILD_STATUS -ne 0 ]; then
+ echo "โ ERROR: Build command failed with exit code $BUILD_STATUS"
+ exit $BUILD_STATUS
+fi
+
+echo ""
+echo "๐ Looking for built application..."
+APP_PATH=$(find "$ARCHIVE_DIR" -name "*.app" -type d | head -n 1)
+
+# Check if the build actually succeeded by verifying the app exists
+if [ -z "$APP_PATH" ] || [ ! -d "$APP_PATH" ]; then
+ echo "โ ERROR: Build failed or did not produce an app bundle!"
+ echo "Check the logs above for build errors."
+
+ # List all files in the archive directory to help debug
+ echo "Contents of archive directory:"
+ find "$ARCHIVE_DIR" -type f -o -type d | sort
+
+ exit 1
+fi
+
+echo "โ
Build completed successfully! Application path:"
+echo "$APP_PATH"
+
+if [ -n "$APP_PATH" ]; then
+ echo ""
+ echo "๐ Binary files summary:"
+ 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))"
+
+ # Check bundle ID (for information only, no modifications)
+ INFO_PLIST="$APP_PATH/Contents/Info.plist"
+ if [ -f "$INFO_PLIST" ]; then
+ BUNDLE_ID=$(/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "$INFO_PLIST")
+ echo ""
+ echo "๐ฆ App Bundle ID: $BUNDLE_ID"
+ fi
+fi
+
+echo ""
+echo "โ
Build complete!"
+echo "App location: $APP_PATH"
diff --git a/win_build.sh b/scripts/win_build.sh
similarity index 100%
rename from win_build.sh
rename to scripts/win_build.sh