WIP: feat(workflows): add new build workflows for Windows, Linux, and macOS, and remove obsolete build scripts #17
46
.gitea/actions/linux-build/action.yml
Normal file
46
.gitea/actions/linux-build/action.yml
Normal file
@ -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
|
132
.gitea/actions/macos-build/action.yml
Normal file
132
.gitea/actions/macos-build/action.yml
Normal file
@ -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
|
704
.gitea/actions/macos-notarize/action.yml
Normal file
704
.gitea/actions/macos-notarize/action.yml
Normal file
@ -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
|
42
.gitea/actions/windows-build/action.yml
Normal file
42
.gitea/actions/windows-build/action.yml
Normal file
@ -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
|
209
.gitea/workflows/build.yml
Normal file
209
.gitea/workflows/build.yml
Normal file
@ -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 '<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>LuckyRobots ${{ env.RELEASE_TAG }} Downloads</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
|
||||||
|
h1 { color: #333; }
|
||||||
|
.download-btn {
|
||||||
|
display: inline-block;
|
||||||
|
background-color: #4CAF50;
|
||||||
|
color: white;
|
||||||
|
padding: 10px 20px;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 10px 5px;
|
||||||
|
}
|
||||||
|
.download-btn:hover { background-color: #45a049; }
|
||||||
|
.platform { margin-bottom: 30px; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>LuckyRobots Game - ${{ env.RELEASE_TAG }}</h1>
|
||||||
|
<p>Build #${{ github.run_number }} - Built from commit: ${{ github.sha }}</p>
|
||||||
|
|
||||||
|
<div class="platform">
|
||||||
|
<h2>Windows</h2>
|
||||||
|
<p><a href="https://luckyrobots.com/luckyrobots/luckyworld/actions/runs/${{ github.run_id }}/artifacts/LuckyRobots-Windows" class="download-btn">Download Windows Build</a></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="platform">
|
||||||
|
<h2>Linux</h2>
|
||||||
|
<p><a href="https://luckyrobots.com/luckyrobots/luckyworld/actions/runs/${{ github.run_id }}/artifacts/LuckyRobots-Linux" class="download-btn">Download Linux Build</a></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="platform">
|
||||||
|
<h2>macOS</h2>
|
||||||
|
<p><a href="https://luckyrobots.com/luckyrobots/luckyworld/actions/runs/${{ github.run_id }}/artifacts/LuckyRobots-macOS" class="download-btn">Download macOS Build</a></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<p>Generated on '$(date -u +"%Y-%m-%d %H:%M:%S UTC")'</p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>' > 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 }}'
|
||||||
|
|
@ -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 }}'
|
|
163
.gitea/workflows/release.yml
Normal file
163
.gitea/workflows/release.yml
Normal file
@ -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 '<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>LuckyRobots ${{ env.RELEASE_TAG }} Downloads</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
|
||||||
|
h1 { color: #333; }
|
||||||
|
.download-btn {
|
||||||
|
display: inline-block;
|
||||||
|
background-color: #4CAF50;
|
||||||
|
color: white;
|
||||||
|
padding: 10px 20px;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 10px 5px;
|
||||||
|
}
|
||||||
|
.download-btn:hover { background-color: #45a049; }
|
||||||
|
.platform { margin-bottom: 30px; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>LuckyRobots Game - ${{ env.RELEASE_TAG }}</h1>
|
||||||
|
<p>Build #${{ github.run_number }} - Built from commit: ${{ github.sha }}</p>
|
||||||
|
|
||||||
|
<div class="platform">
|
||||||
|
<h2>Windows</h2>
|
||||||
|
<p><a href="https://luckyrobots.com/luckyrobots/luckyworld/actions/runs/${{ github.run_id }}/artifacts/LuckyRobots-Windows" class="download-btn">Download Windows Build</a></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="platform">
|
||||||
|
<h2>Linux</h2>
|
||||||
|
<p><a href="https://luckyrobots.com/luckyrobots/luckyworld/actions/runs/${{ github.run_id }}/artifacts/LuckyRobots-Linux" class="download-btn">Download Linux Build</a></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="platform">
|
||||||
|
<h2>macOS</h2>
|
||||||
|
<p><a href="https://luckyrobots.com/luckyrobots/luckyworld/actions/runs/${{ github.run_id }}/artifacts/LuckyRobots-macOS" class="download-btn">Download macOS Build</a></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<p>Generated on '$(date -u +"%Y-%m-%d %H:%M:%S UTC")'</p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>' > 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 }}'
|
392
.gitea/workflows/test-local-signing.yml
Normal file
392
.gitea/workflows/test-local-signing.yml
Normal file
@ -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
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.cs.allow-jit</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.cs.disable-library-validation</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.device.audio-input</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.device.camera</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.automation.apple-events</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.get-task-allow</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
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
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>TestApp</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>com.luckyrobots.luckyworld.testapp</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>TestApp</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>LSMinimumSystemVersion</key>
|
||||||
|
<string>10.10</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
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
|
873
.gitea/workflows/test-macos-build.yml
Normal file
873
.gitea/workflows/test-macos-build.yml
Normal file
@ -0,0 +1,873 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# 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 debug log helper function
|
||||||
|
echo '
|
||||||
|
# Helper function for debug logging
|
||||||
|
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
|
||||||
|
}
|
||||||
|
' > /tmp/debug_helpers.sh
|
||||||
|
chmod +x /tmp/debug_helpers.sh
|
||||||
|
|
||||||
|
# Log debug message directly
|
||||||
|
echo "DEBUG: Starting macOS notarize action"
|
||||||
|
echo "$(date "+%Y-%m-%d %H:%M:%S") - Starting macOS notarize action" >> "$DEBUG_LOG_PATH"
|
||||||
|
echo "DEBUG: App path: ${{ env.APP_PATH }}"
|
||||||
|
echo "$(date "+%Y-%m-%d %H:%M:%S") - App path: ${{ env.APP_PATH }}" >> "$DEBUG_LOG_PATH"
|
||||||
|
echo "DEBUG: Team ID: ${{ secrets.APPLE_TEAM_ID }}"
|
||||||
|
echo "$(date "+%Y-%m-%d %H:%M:%S") - Team ID: ${{ secrets.APPLE_TEAM_ID }}" >> "$DEBUG_LOG_PATH"
|
||||||
|
echo "DEBUG: Notarization method: api-key"
|
||||||
|
echo "$(date "+%Y-%m-%d %H:%M:%S") - Notarization method: api-key" >> "$DEBUG_LOG_PATH"
|
||||||
|
echo "DEBUG: Bundle ID: ${{ env.BUNDLE_ID }}"
|
||||||
|
echo "$(date "+%Y-%m-%d %H:%M:%S") - Bundle ID: ${{ env.BUNDLE_ID }}" >> "$DEBUG_LOG_PATH"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Set up variables
|
||||||
|
id: setup
|
||||||
|
run: |
|
||||||
|
# Debug log helper
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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
|
||||||
|
debug_log "Using API key method for notarization"
|
||||||
|
|
||||||
|
# Create API key file - properly decode from base64
|
||||||
|
API_KEY_FILE="$WORK_DIR/api_key.p8"
|
||||||
|
debug_log "Decoding API key from base64 to: $API_KEY_FILE"
|
||||||
|
|
||||||
|
# Check if NOTARY_API_KEY_PATH is provided
|
||||||
|
if [[ -z "${{ secrets.NOTARY_API_KEY_PATH }}" ]]; then
|
||||||
|
debug_log "ERROR: NOTARY_API_KEY_PATH secret is empty"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Explicitly decode from base64 as instructed
|
||||||
|
echo "${{ secrets.NOTARY_API_KEY_PATH }}" | base64 -d > "$API_KEY_FILE" 2>/dev/null
|
||||||
|
|
||||||
|
# Verify API key file exists and has content
|
||||||
|
if [[ ! -f "$API_KEY_FILE" ]]; then
|
||||||
|
debug_log "ERROR: API key file could not be created"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -s "$API_KEY_FILE" ]]; then
|
||||||
|
debug_log "ERROR: API key file is empty after base64 decoding"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify key format
|
||||||
|
if ! grep -q "BEGIN PRIVATE KEY" "$API_KEY_FILE"; then
|
||||||
|
debug_log "ERROR: API key is not in PEM format (missing BEGIN PRIVATE KEY)"
|
||||||
|
debug_log "First 10 bytes of API key file: $(hexdump -n 10 -ve '1/1 "%.2x"' "$API_KEY_FILE")"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get file permissions and size for debugging
|
||||||
|
FILE_PERMS=$(ls -la "$API_KEY_FILE")
|
||||||
|
FILE_SIZE=$(wc -c < "$API_KEY_FILE")
|
||||||
|
debug_log "API key file ($FILE_SIZE bytes): $FILE_PERMS"
|
||||||
|
|
||||||
|
# Set proper permissions
|
||||||
|
chmod 600 "$API_KEY_FILE"
|
||||||
|
|
||||||
|
echo "API_KEY_FILE=$API_KEY_FILE" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
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 helper
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
# Add to search list and set as default
|
||||||
|
security list-keychains -d user -s "$KEYCHAIN_NAME" login.keychain
|
||||||
|
security default-keychain -s "$KEYCHAIN_NAME"
|
||||||
|
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
|
||||||
|
|
||||||
|
# Allow codesign to access keychain items without prompting
|
||||||
|
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
|
||||||
|
|
||||||
|
# List all identities to find the exact name
|
||||||
|
debug_log "Listing all identities in the keychain:"
|
||||||
|
IDENTITY_INFO=$(security find-identity -v "$KEYCHAIN_NAME")
|
||||||
|
debug_log "$IDENTITY_INFO"
|
||||||
|
|
||||||
|
# Parse the exact identity name from the output
|
||||||
|
EXACT_IDENTITY=$(echo "$IDENTITY_INFO" | grep "Developer ID Application" | head -1 | sed -E 's/.*"(Developer ID Application: .*)"/\1/')
|
||||||
|
|
||||||
|
if [[ -n "$EXACT_IDENTITY" ]]; then
|
||||||
|
debug_log "Found exact identity: $EXACT_IDENTITY"
|
||||||
|
SIGNING_IDENTITY="$EXACT_IDENTITY"
|
||||||
|
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 helper
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_log "Starting application signing process"
|
||||||
|
|
||||||
|
# Make sure keychain is unlocked and available
|
||||||
|
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
|
||||||
|
security list-keychains
|
||||||
|
security default-keychain
|
||||||
|
|
||||||
|
# Verify certificate exists
|
||||||
|
IDENTITY_INFO=$(security find-identity -v "$KEYCHAIN_NAME")
|
||||||
|
EXACT_IDENTITY=$(echo "$IDENTITY_INFO" | grep "Developer ID Application" | head -1 | sed -E 's/.*"(Developer ID Application: .*)"/\1/')
|
||||||
|
|
||||||
|
if [[ -z "$EXACT_IDENTITY" ]]; then
|
||||||
|
debug_log "ERROR: No Developer ID Application certificate found in keychain"
|
||||||
|
debug_log "$IDENTITY_INFO"
|
||||||
|
echo "SIGNING_RESULT=false" >> $GITHUB_ENV
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
debug_log "Found signing identity: $EXACT_IDENTITY"
|
||||||
|
SIGNING_IDENTITY="$EXACT_IDENTITY"
|
||||||
|
|
||||||
|
# Get hash ID if available for direct signing
|
||||||
|
if [[ "$IDENTITY_INFO" =~ ([0-9A-F]{40}) ]]; then
|
||||||
|
HASH_ID="${BASH_REMATCH[1]}"
|
||||||
|
debug_log "Using certificate hash: $HASH_ID"
|
||||||
|
else
|
||||||
|
HASH_ID=""
|
||||||
|
debug_log "No certificate hash found, using identity name"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check entitlements file and validate it
|
||||||
|
ENTITLEMENTS_PATH="${{ env.ENTITLEMENTS_FILE }}"
|
||||||
|
USE_ENTITLEMENTS=false
|
||||||
|
|
||||||
|
if [[ -f "$ENTITLEMENTS_PATH" ]]; then
|
||||||
|
debug_log "Found entitlements file: $ENTITLEMENTS_PATH - validating..."
|
||||||
|
|
||||||
|
# Try to validate the entitlements file
|
||||||
|
plutil -lint "$ENTITLEMENTS_PATH" > /dev/null 2>&1
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
debug_log "Entitlements file is valid, will use for signing"
|
||||||
|
USE_ENTITLEMENTS=true
|
||||||
|
else
|
||||||
|
debug_log "WARNING: Entitlements file is invalid, will sign without entitlements"
|
||||||
|
# Print the entitlements file content for debugging
|
||||||
|
debug_log "Entitlements file content:"
|
||||||
|
cat "$ENTITLEMENTS_PATH" | tee -a "$DEBUG_LOG_PATH"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
debug_log "No entitlements file found at $ENTITLEMENTS_PATH, will sign without entitlements"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# First remove existing signatures
|
||||||
|
debug_log "Removing existing signatures..."
|
||||||
|
codesign --remove-signature "$APP_PATH" || true
|
||||||
|
|
||||||
|
# Sign embedded components first
|
||||||
|
debug_log "Signing embedded components..."
|
||||||
|
|
||||||
|
# Sign MacOS directory contents
|
||||||
|
if [ -d "$APP_PATH/Contents/MacOS" ]; then
|
||||||
|
debug_log "Signing MacOS executables and libraries..."
|
||||||
|
# Sign dylibs first
|
||||||
|
if [ "$USE_ENTITLEMENTS" = true ]; then
|
||||||
|
find "$APP_PATH/Contents/MacOS" -type f -name "*.dylib" -exec codesign --force --timestamp --options runtime --entitlements "$ENTITLEMENTS_PATH" --sign "$SIGNING_IDENTITY" {} \; 2>/dev/null || true
|
||||||
|
else
|
||||||
|
find "$APP_PATH/Contents/MacOS" -type f -name "*.dylib" -exec codesign --force --timestamp --options runtime --sign "$SIGNING_IDENTITY" {} \; 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Sign all executables
|
||||||
|
if [ "$USE_ENTITLEMENTS" = true ]; then
|
||||||
|
find "$APP_PATH/Contents/MacOS" -type f -perm +111 -exec codesign --force --timestamp --options runtime --entitlements "$ENTITLEMENTS_PATH" --sign "$SIGNING_IDENTITY" {} \; 2>/dev/null || true
|
||||||
|
else
|
||||||
|
find "$APP_PATH/Contents/MacOS" -type f -perm +111 -exec codesign --force --timestamp --options runtime --sign "$SIGNING_IDENTITY" {} \; 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Sign any remaining files
|
||||||
|
if [ "$USE_ENTITLEMENTS" = true ]; then
|
||||||
|
find "$APP_PATH/Contents/MacOS" -type f -not -name "*.dylib" -not -perm +111 -exec codesign --force --timestamp --options runtime --entitlements "$ENTITLEMENTS_PATH" --sign "$SIGNING_IDENTITY" {} \; 2>/dev/null || true
|
||||||
|
else
|
||||||
|
find "$APP_PATH/Contents/MacOS" -type f -not -name "*.dylib" -not -perm +111 -exec codesign --force --timestamp --options runtime --sign "$SIGNING_IDENTITY" {} \; 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
debug_log "No MacOS directory found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Sign Frameworks if they exist
|
||||||
|
if [ -d "$APP_PATH/Contents/Frameworks" ]; then
|
||||||
|
debug_log "Signing Frameworks directory..."
|
||||||
|
# Sign individual files in Frameworks
|
||||||
|
if [ "$USE_ENTITLEMENTS" = true ]; then
|
||||||
|
find "$APP_PATH/Contents/Frameworks" -type f -not -path "*.framework/*" -exec codesign --force --timestamp --options runtime --entitlements "$ENTITLEMENTS_PATH" --sign "$SIGNING_IDENTITY" {} \; 2>/dev/null || true
|
||||||
|
# Sign framework bundles
|
||||||
|
find "$APP_PATH/Contents/Frameworks" -type d -name "*.framework" -exec codesign --force --timestamp --options runtime --entitlements "$ENTITLEMENTS_PATH" --sign "$SIGNING_IDENTITY" {} \; 2>/dev/null || true
|
||||||
|
else
|
||||||
|
find "$APP_PATH/Contents/Frameworks" -type f -not -path "*.framework/*" -exec codesign --force --timestamp --options runtime --sign "$SIGNING_IDENTITY" {} \; 2>/dev/null || true
|
||||||
|
# Sign framework bundles
|
||||||
|
find "$APP_PATH/Contents/Frameworks" -type d -name "*.framework" -exec codesign --force --timestamp --options runtime --sign "$SIGNING_IDENTITY" {} \; 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
debug_log "No Frameworks directory found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Sign any plugins if they exist
|
||||||
|
if [ -d "$APP_PATH/Contents/PlugIns" ]; then
|
||||||
|
debug_log "Signing PlugIns directory..."
|
||||||
|
if [ "$USE_ENTITLEMENTS" = true ]; then
|
||||||
|
find "$APP_PATH/Contents/PlugIns" -type d -name "*.appex" -exec codesign --force --timestamp --options runtime --entitlements "$ENTITLEMENTS_PATH" --sign "$SIGNING_IDENTITY" {} \; 2>/dev/null || true
|
||||||
|
else
|
||||||
|
find "$APP_PATH/Contents/PlugIns" -type d -name "*.appex" -exec codesign --force --timestamp --options runtime --sign "$SIGNING_IDENTITY" {} \; 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Sign main app bundle (use hash ID if available, otherwise use identity name)
|
||||||
|
debug_log "Signing main app bundle..."
|
||||||
|
if [ "$USE_ENTITLEMENTS" = true ]; then
|
||||||
|
debug_log "Using entitlements file: $ENTITLEMENTS_PATH"
|
||||||
|
if [[ -n "$HASH_ID" ]]; then
|
||||||
|
codesign --force --deep --timestamp --options runtime --entitlements "$ENTITLEMENTS_PATH" --sign "$HASH_ID" "$APP_PATH"
|
||||||
|
else
|
||||||
|
codesign --force --deep --timestamp --options runtime --entitlements "$ENTITLEMENTS_PATH" --sign "$SIGNING_IDENTITY" "$APP_PATH"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
debug_log "Signing without entitlements"
|
||||||
|
if [[ -n "$HASH_ID" ]]; then
|
||||||
|
codesign --force --deep --timestamp --options runtime --sign "$HASH_ID" "$APP_PATH"
|
||||||
|
else
|
||||||
|
codesign --force --deep --timestamp --options runtime --sign "$SIGNING_IDENTITY" "$APP_PATH"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
SIGN_RESULT=$?
|
||||||
|
if [ $SIGN_RESULT -eq 0 ]; then
|
||||||
|
debug_log "App signed successfully"
|
||||||
|
echo "SIGNING_RESULT=true" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
# Verify signature
|
||||||
|
debug_log "Verifying app signature..."
|
||||||
|
codesign -dvv "$APP_PATH"
|
||||||
|
VERIFY_RESULT=$?
|
||||||
|
|
||||||
|
if [ $VERIFY_RESULT -eq 0 ]; then
|
||||||
|
debug_log "Signature verification successful"
|
||||||
|
else
|
||||||
|
debug_log "WARNING: Signature verification failed, app may not be properly signed"
|
||||||
|
# Continue anyway since the signing appeared to succeed
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
debug_log "ERROR: App signing failed with exit code: $SIGN_RESULT"
|
||||||
|
echo "SIGNING_RESULT=false" >> $GITHUB_ENV
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Notarize application
|
||||||
|
id: notarize-app
|
||||||
|
if: env.SIGNING_RESULT == 'true'
|
||||||
|
run: |
|
||||||
|
# Debug log helper
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
debug_log "ZIP archive created successfully at: $ZIP_PATH"
|
||||||
|
ZIP_SIZE=$(du -h "$ZIP_PATH" | cut -f1)
|
||||||
|
debug_log "ZIP archive size: $ZIP_SIZE"
|
||||||
|
|
||||||
|
# Submit for notarization - use separate submission and polling
|
||||||
|
debug_log "Submitting app for notarization..."
|
||||||
|
|
||||||
|
# Create submission file command from parts to avoid shell re-interpretation
|
||||||
|
SUBMIT_CMD="xcrun notarytool submit \"$ZIP_PATH\" --key \"$API_KEY_FILE\" --key-id \"${{ secrets.NOTARY_API_KEY_ID }}\" --issuer \"${{ secrets.NOTARY_API_KEY_ISSUER_ID }}\""
|
||||||
|
debug_log "Running command: $SUBMIT_CMD"
|
||||||
|
|
||||||
|
# Submit with error capture
|
||||||
|
SUBMIT_OUTPUT=$(eval "$SUBMIT_CMD" 2>&1)
|
||||||
|
SUBMIT_STATUS=$?
|
||||||
|
|
||||||
|
# Save output for detailed analysis
|
||||||
|
echo "$SUBMIT_OUTPUT" > "$WORK_DIR/submit_output.txt"
|
||||||
|
cat "$WORK_DIR/submit_output.txt" | tee -a "$DEBUG_LOG_PATH"
|
||||||
|
|
||||||
|
if [ $SUBMIT_STATUS -ne 0 ]; then
|
||||||
|
debug_log "ERROR: Failed to submit for notarization, exit code: $SUBMIT_STATUS"
|
||||||
|
echo "NOTARIZATION_RESULT=false" >> $GITHUB_ENV
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract the request UUID (handle different output formats)
|
||||||
|
REQUEST_UUID=$(echo "$SUBMIT_OUTPUT" | grep -o "id: [a-z0-9-]*" | cut -d' ' -f2)
|
||||||
|
|
||||||
|
if [ -z "$REQUEST_UUID" ]; then
|
||||||
|
# Alternative grep pattern
|
||||||
|
REQUEST_UUID=$(echo "$SUBMIT_OUTPUT" | grep -o "[a-f0-9]\{8\}-[a-f0-9]\{4\}-[a-f0-9]\{4\}-[a-f0-9]\{4\}-[a-f0-9]\{12\}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$REQUEST_UUID" ]; then
|
||||||
|
debug_log "ERROR: Failed to extract UUID from notarization submission"
|
||||||
|
debug_log "Full submission output:"
|
||||||
|
cat "$WORK_DIR/submit_output.txt"
|
||||||
|
echo "NOTARIZATION_RESULT=false" >> $GITHUB_ENV
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
debug_log "Notarization submission UUID: $REQUEST_UUID"
|
||||||
|
echo "NOTARIZATION_UUID=$REQUEST_UUID" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
# Wait for notarization to complete with polling
|
||||||
|
debug_log "Waiting for notarization to complete (this may take several minutes)..."
|
||||||
|
|
||||||
|
WAIT_COUNTER=1
|
||||||
|
TOTAL_WAIT=60 # 60 minutes maximum
|
||||||
|
|
||||||
|
while [ $WAIT_COUNTER -le $TOTAL_WAIT ]; do
|
||||||
|
if [ $WAIT_COUNTER -gt 1 ]; then
|
||||||
|
debug_log "Waiting 60 seconds before checking again (attempt $WAIT_COUNTER/$TOTAL_WAIT)..."
|
||||||
|
sleep 60
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check status
|
||||||
|
STATUS_CMD="xcrun notarytool info \"$REQUEST_UUID\" --key \"$API_KEY_FILE\" --key-id \"${{ secrets.NOTARY_API_KEY_ID }}\" --issuer \"${{ secrets.NOTARY_API_KEY_ISSUER_ID }}\""
|
||||||
|
debug_log "Checking status with: $STATUS_CMD"
|
||||||
|
STATUS_OUTPUT=$(eval "$STATUS_CMD" 2>&1)
|
||||||
|
STATUS_CODE=$?
|
||||||
|
|
||||||
|
echo "$STATUS_OUTPUT" > "$WORK_DIR/status_output_$WAIT_COUNTER.txt"
|
||||||
|
cat "$WORK_DIR/status_output_$WAIT_COUNTER.txt" | tee -a "$DEBUG_LOG_PATH"
|
||||||
|
|
||||||
|
if [ $STATUS_CODE -ne 0 ]; then
|
||||||
|
debug_log "WARNING: Status check failed, exit code: $STATUS_CODE"
|
||||||
|
# Continue anyway to retry
|
||||||
|
else
|
||||||
|
# Extract status
|
||||||
|
REQUEST_STATUS=$(echo "$STATUS_OUTPUT" | grep -o "status: [A-Za-z]*" | cut -d' ' -f2)
|
||||||
|
|
||||||
|
if [ -z "$REQUEST_STATUS" ]; then
|
||||||
|
debug_log "WARNING: Could not extract status from output"
|
||||||
|
else
|
||||||
|
debug_log "Notarization status: $REQUEST_STATUS"
|
||||||
|
|
||||||
|
if [ "$REQUEST_STATUS" = "Accepted" ]; then
|
||||||
|
debug_log "Notarization successful!"
|
||||||
|
echo "NOTARIZATION_RESULT=true" >> $GITHUB_ENV
|
||||||
|
break
|
||||||
|
elif [ "$REQUEST_STATUS" = "Invalid" ] || [ "$REQUEST_STATUS" = "Rejected" ]; then
|
||||||
|
debug_log "ERROR: Notarization failed with status: $REQUEST_STATUS"
|
||||||
|
echo "NOTARIZATION_RESULT=false" >> $GITHUB_ENV
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# In progress - continue waiting
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
WAIT_COUNTER=$((WAIT_COUNTER+1))
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check final status
|
||||||
|
if [ $WAIT_COUNTER -gt $TOTAL_WAIT ]; then
|
||||||
|
debug_log "ERROR: Notarization wait timeout after $TOTAL_WAIT minutes"
|
||||||
|
echo "NOTARIZATION_RESULT=false" >> $GITHUB_ENV
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$REQUEST_STATUS" != "Accepted" ]; then
|
||||||
|
debug_log "ERROR: Notarization failed or timed out"
|
||||||
|
echo "NOTARIZATION_RESULT=false" >> $GITHUB_ENV
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Success - now staple the ticket
|
||||||
|
debug_log "Notarization complete, proceeding to 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
|
||||||
|
xcrun stapler validate "$APP_PATH"
|
||||||
|
VALIDATE_RESULT=$?
|
||||||
|
|
||||||
|
if [ $VALIDATE_RESULT -eq 0 ]; then
|
||||||
|
debug_log "Stapling validation successful"
|
||||||
|
echo "VERIFY_RESULT=true" >> $GITHUB_ENV
|
||||||
|
else
|
||||||
|
debug_log "WARNING: Stapling validation failed but continuing"
|
||||||
|
echo "VERIFY_RESULT=false" >> $GITHUB_ENV
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
debug_log "ERROR: Failed to staple notarization ticket"
|
||||||
|
echo "STAPLING_RESULT=false" >> $GITHUB_ENV
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Staple notarization ticket
|
||||||
|
id: staple-ticket
|
||||||
|
if: env.SIGNING_RESULT == 'true' && env.NOTARIZATION_RESULT == 'true'
|
||||||
|
run: |
|
||||||
|
# Debug log helper
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
else
|
||||||
|
debug_log "ERROR: Stapling failed with exit code: $STAPLE_RESULT"
|
||||||
|
echo "STAPLING_RESULT=false" >> $GITHUB_ENV
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Verify notarization and stapling
|
||||||
|
id: verify-notarization
|
||||||
|
if: env.SIGNING_RESULT == 'true' && env.NOTARIZATION_RESULT == 'true' && env.STAPLING_RESULT == 'true'
|
||||||
|
run: |
|
||||||
|
# Debug log helper
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_log "Verifying notarization and stapling"
|
||||||
|
|
||||||
|
# Verify code signature with staple
|
||||||
|
debug_log "Verifying code signature..."
|
||||||
|
codesign --verify --deep --strict --verbose=2 "$APP_PATH"
|
||||||
|
VERIFY_RESULT=$?
|
||||||
|
|
||||||
|
if [ $VERIFY_RESULT -eq 0 ]; then
|
||||||
|
debug_log "Code signature verification successful"
|
||||||
|
else
|
||||||
|
debug_log "ERROR: Code signature verification failed with exit code: $VERIFY_RESULT"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify stapling
|
||||||
|
debug_log "Verifying stapling..."
|
||||||
|
xcrun stapler validate "$APP_PATH"
|
||||||
|
STAPLE_VERIFY_RESULT=$?
|
||||||
|
|
||||||
|
if [ $STAPLE_VERIFY_RESULT -eq 0 ]; then
|
||||||
|
debug_log "Stapling verification successful"
|
||||||
|
echo "VERIFY_RESULT=true" >> $GITHUB_ENV
|
||||||
|
else
|
||||||
|
debug_log "ERROR: Stapling verification failed with exit code: $STAPLE_VERIFY_RESULT"
|
||||||
|
echo "VERIFY_RESULT=false" >> $GITHUB_ENV
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Remove quarantine attribute
|
||||||
|
id: remove-quarantine
|
||||||
|
if: env.SIGNING_RESULT == 'true' && env.NOTARIZATION_RESULT == 'true' && env.STAPLING_RESULT == 'true'
|
||||||
|
run: |
|
||||||
|
# Debug log helper
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
if: env.SIGNING_RESULT == 'true' && env.NOTARIZATION_RESULT == 'true' && env.STAPLING_RESULT == 'true'
|
||||||
|
run: |
|
||||||
|
# Debug log helper
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_log "Packaging the signed and notarized app"
|
||||||
|
|
||||||
|
# Create DMG using hdiutil
|
||||||
|
debug_log "Creating DMG package..."
|
||||||
|
hdiutil create -volname "$APP_NAME" -srcfolder "$APP_PATH" -ov -format UDZO "$DMG_PATH"
|
||||||
|
DMG_CREATE_RESULT=$?
|
||||||
|
|
||||||
|
# Check DMG creation result
|
||||||
|
if [ $DMG_CREATE_RESULT -eq 0 ]; then
|
||||||
|
debug_log "DMG package created successfully at: $DMG_PATH"
|
||||||
|
DMG_SIZE=$(du -h "$DMG_PATH" | cut -f1)
|
||||||
|
debug_log "DMG size: $DMG_SIZE"
|
||||||
|
echo "DMG_CREATED=true" >> $GITHUB_ENV
|
||||||
|
echo "PACKAGE_PATH=$DMG_PATH" >> $GITHUB_ENV
|
||||||
|
else
|
||||||
|
debug_log "WARNING: DMG creation failed, using ZIP as distribution package"
|
||||||
|
echo "DMG_CREATED=false" >> $GITHUB_ENV
|
||||||
|
echo "PACKAGE_PATH=$ZIP_PATH" >> $GITHUB_ENV
|
||||||
|
fi
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Clean up
|
||||||
|
id: cleanup
|
||||||
|
if: always()
|
||||||
|
run: |
|
||||||
|
# Debug log helper
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_log "Cleaning up resources"
|
||||||
|
|
||||||
|
# Save state message for better debugging
|
||||||
|
if [[ "$NOTARIZATION_RESULT" == "true" ]]; then
|
||||||
|
debug_log "Cleanup after successful notarization"
|
||||||
|
else
|
||||||
|
debug_log "Cleanup after notarization state: $NOTARIZATION_RESULT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 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
|
||||||
|
# Save notarization logs first
|
||||||
|
if [[ -d "$DEBUG_LOG_PATH" ]] && [[ -d "$WORK_DIR" ]]; then
|
||||||
|
debug_log "Saving work directory files to debug logs"
|
||||||
|
cp -R "$WORK_DIR/"*.txt "$DEBUG_LOG_PATH/" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
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
|
||||||
|
|
@ -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 '<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>LuckyRobots ${{ env.RELEASE_TAG }} Downloads</title>
|
|
||||||
<style>
|
|
||||||
body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
|
|
||||||
h1 { color: #333; }
|
|
||||||
.download-btn {
|
|
||||||
display: inline-block;
|
|
||||||
background-color: #4CAF50;
|
|
||||||
color: white;
|
|
||||||
padding: 10px 20px;
|
|
||||||
text-decoration: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin: 10px 5px;
|
|
||||||
}
|
|
||||||
.download-btn:hover { background-color: #45a049; }
|
|
||||||
.platform { margin-bottom: 30px; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>LuckyRobots Game - ${{ env.RELEASE_TAG }}</h1>
|
|
||||||
<p>Build #${{ github.run_number }} - Built from commit: ${{ github.sha }}</p>
|
|
||||||
|
|
||||||
<div class="platform">
|
|
||||||
<h2>Windows</h2>
|
|
||||||
<p><a href="https://luckyrobots.com/luckyrobots/luckyworld/actions/runs/${{ github.run_id }}/artifacts/LuckyRobots-Windows" class="download-btn">Download Windows Build</a></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="platform">
|
|
||||||
<h2>Linux</h2>
|
|
||||||
<p><a href="https://luckyrobots.com/luckyrobots/luckyworld/actions/runs/${{ github.run_id }}/artifacts/LuckyRobots-Linux" class="download-btn">Download Linux Build</a></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<footer>
|
|
||||||
<p>Generated on '$(date -u +"%Y-%m-%d %H:%M:%S UTC")'</p>
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
</html>' > 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
|
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -41,3 +41,4 @@ DerivedDataCache/*
|
|||||||
#this only is the Binaries folder on the root, not the Binaries folder in the plugin folders
|
#this only is the Binaries folder on the root, not the Binaries folder in the plugin folders
|
||||||
Binaries/**
|
Binaries/**
|
||||||
*.app/
|
*.app/
|
||||||
|
.cursorrules
|
||||||
|
@ -338,3 +338,35 @@ NearClipPlane=0.100000
|
|||||||
bFinalUsesRDO=True
|
bFinalUsesRDO=True
|
||||||
FinalRDOLambda=100
|
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")
|
||||||
|
|
||||||
|
@ -7,6 +7,9 @@ ProjectVersion=0.1
|
|||||||
;bAddPacks=True
|
;bAddPacks=True
|
||||||
;InsertPack=(PackSource="StarterContent.upack",PackName="StarterContent")
|
;InsertPack=(PackSource="StarterContent.upack",PackName="StarterContent")
|
||||||
|
|
||||||
|
[/Script/MacTargetPlatform.MacTargetSettings]
|
||||||
|
BundleIdentifier=com.luckyrobots.luckyworld
|
||||||
|
|
||||||
[/Script/UnrealEd.ProjectPackagingSettings]
|
[/Script/UnrealEd.ProjectPackagingSettings]
|
||||||
Build=IfProjectHasCode
|
Build=IfProjectHasCode
|
||||||
BuildConfiguration=PPBC_Shipping
|
BuildConfiguration=PPBC_Shipping
|
||||||
|
28
LuckyWorld.entitlements
Normal file
28
LuckyWorld.entitlements
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.cs.allow-jit</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.cs.disable-library-validation</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.device.audio-input</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.device.camera</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.automation.apple-events</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.cs.debugger</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.files.user-selected.read-write</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.network.client</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.get-task-allow</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
@ -19,5 +19,6 @@ public class LuckyWorld : ModuleRules
|
|||||||
// PrivateDependencyModuleNames.Add("OnlineSubsystem");
|
// PrivateDependencyModuleNames.Add("OnlineSubsystem");
|
||||||
|
|
||||||
// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
|
// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
55
mac_build.sh
55
mac_build.sh
@ -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)
|
|
131
scripts/mac_build.sh
Executable file
131
scripts/mac_build.sh
Executable file
@ -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"
|
Loading…
x
Reference in New Issue
Block a user