name: Test Local Signing on: workflow_dispatch: # Manual trigger # push: # branches: [ozgur/build] jobs: test-local-signing: runs-on: macos steps: - name: Checkout repository uses: actions/checkout@v3 - name: Create Test Entitlements run: | echo "๐Ÿ“ Creating entitlements file..." cat > LuckyWorld.entitlements << EOF com.apple.security.cs.allow-jit com.apple.security.cs.allow-unsigned-executable-memory com.apple.security.cs.disable-library-validation com.apple.security.cs.allow-dyld-environment-variables com.apple.security.device.audio-input com.apple.security.device.camera EOF echo "โœ… Created entitlements file" cat LuckyWorld.entitlements shell: bash - name: Create Test App Bundle run: | echo "๐Ÿ“ฆ Creating test app bundle..." # Create test app bundle structure TEST_APP_DIR="TestApp.app" mkdir -p "$TEST_APP_DIR/Contents/MacOS" # Create a simple test executable echo '#!/bin/bash echo "Hello from TestApp!"' > "$TEST_APP_DIR/Contents/MacOS/TestApp" chmod +x "$TEST_APP_DIR/Contents/MacOS/TestApp" # Create Info.plist cat > "$TEST_APP_DIR/Contents/Info.plist" << EOF CFBundleExecutable TestApp CFBundleIdentifier com.luckyrobots.luckyworld.testapp CFBundleName TestApp CFBundlePackageType APPL CFBundleShortVersionString 1.0 LSMinimumSystemVersion 10.10 EOF echo "โœ… Created test app bundle" # Verify app bundle exists if [ ! -d "$TEST_APP_DIR" ]; then echo "โŒ Error: App bundle not found at $TEST_APP_DIR" exit 1 fi echo "๐Ÿ” App bundle contents:" ls -la "$TEST_APP_DIR" # Store app path as environment variable echo "APP_PATH=$(pwd)/TestApp.app" >> "$GITHUB_ENV" shell: bash - name: Setup Certificate env: CERTIFICATE_BASE64: ${{ secrets.MACOS_CERTIFICATE }} CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PWD }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} run: | echo "๐Ÿ” Setting up certificate..." # Create a temporary directory for certificates CERT_DIR="$HOME/certificates" mkdir -p "$CERT_DIR" # Decode the certificate to a p12 file echo "$CERTIFICATE_BASE64" | base64 --decode > "$CERT_DIR/certificate.p12" # Check certificate format and details echo "๐Ÿ“‘ Certificate format check:" file "$CERT_DIR/certificate.p12" # Try to get certificate info with openssl echo "๐Ÿ“‘ Certificate info with OpenSSL:" openssl pkcs12 -info -in "$CERT_DIR/certificate.p12" -nokeys -passin pass:"$CERTIFICATE_PASSWORD" || echo "Failed to read certificate with OpenSSL" # Create keychain KEYCHAIN_PATH="$CERT_DIR/app-signing.keychain-db" KEYCHAIN_PASSWORD="temppassword123" # Delete existing keychain if it exists security delete-keychain "$KEYCHAIN_PATH" 2>/dev/null || true # Create new keychain security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" security set-keychain-settings -t 3600 -u -l "$KEYCHAIN_PATH" security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" # Add to search list and make default security list-keychains -d user -s "$KEYCHAIN_PATH" $(security list-keychains -d user | tr -d '"') security default-keychain -s "$KEYCHAIN_PATH" # Try multiple import approaches echo "๐Ÿ”‘ Importing developer certificate - attempt 1 (standard)..." security import "$CERT_DIR/certificate.p12" -k "$KEYCHAIN_PATH" -P "$CERTIFICATE_PASSWORD" -T /usr/bin/codesign echo "๐Ÿ”‘ Importing developer certificate - attempt 2 (with flags)..." security import "$CERT_DIR/certificate.p12" -k "$KEYCHAIN_PATH" -P "$CERTIFICATE_PASSWORD" -T /usr/bin/codesign -x -A echo "๐Ÿ”‘ Importing developer certificate - attempt 3 (with format)..." security import "$CERT_DIR/certificate.p12" -k "$KEYCHAIN_PATH" -P "$CERTIFICATE_PASSWORD" -T /usr/bin/codesign -f pkcs12 # Set partition list for codesign to access keychain security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" # Check all certificates in keychain echo "๐Ÿ” Listing all certificates in keychain..." security find-certificate -a "$KEYCHAIN_PATH" # Verify certificate echo "๐Ÿ” Verifying code signing identities..." security find-identity -v -p codesigning "$KEYCHAIN_PATH" # Alternative check for identities echo "๐Ÿ” Listing identities with code signing usage..." security find-certificate -a -c "Developer ID Application" -p "$KEYCHAIN_PATH" | grep -q "Code Signing" && echo "โœ… Certificate has code signing usage" || echo "โŒ Certificate does NOT have code signing usage" # Try to use the System keychain as a fallback echo "๐Ÿ” Checking system keychain for code signing identities..." SYSTEM_IDENTITIES=$(security find-identity -v -p codesigning) echo "$SYSTEM_IDENTITIES" if echo "$SYSTEM_IDENTITIES" | grep -q "Developer ID Application"; then echo "โœ… Found Developer ID Application certificate in system keychain" echo "USE_SYSTEM_CERT=true" >> "$GITHUB_ENV" else echo "โŒ No Developer ID Application certificate found in system keychain" echo "USE_SYSTEM_CERT=false" >> "$GITHUB_ENV" fi # Store keychain variables for later steps echo "KEYCHAIN_PATH=$KEYCHAIN_PATH" >> "$GITHUB_ENV" echo "KEYCHAIN_PASSWORD=$KEYCHAIN_PASSWORD" >> "$GITHUB_ENV" echo "APPLE_TEAM_ID=$APPLE_TEAM_ID" >> "$GITHUB_ENV" # Debug: keep p12 file for inspection echo "๐Ÿ’พ Keeping certificate.p12 for debugging" shell: bash - name: Debug Certificate Content if: always() env: CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PWD }} run: | echo "๐Ÿ”Ž Debugging certificate content..." CERT_DIR="$HOME/certificates" # Check if p12 file exists if [ ! -f "$CERT_DIR/certificate.p12" ]; then echo "โŒ Certificate file not found" exit 0 fi # Try with OpenSSL to extract certificate info echo "Attempting to extract certificate info..." openssl pkcs12 -in "$CERT_DIR/certificate.p12" -info -nokeys -passin pass:"$CERTIFICATE_PASSWORD" > cert_info.txt || echo "Failed to extract info" # Check certificate contents echo "Certificate subject information:" grep "subject" cert_info.txt || echo "No subject information found" echo "Certificate issuer information:" grep "issuer" cert_info.txt || echo "No issuer information found" # Check if it's a Developer ID certificate if grep -q "Developer ID" cert_info.txt; then echo "โœ… This appears to be a Developer ID certificate" else echo "โŒ This does NOT appear to be a Developer ID certificate" fi # Check if it has a private key echo "Checking for private key..." if openssl pkcs12 -in "$CERT_DIR/certificate.p12" -nocerts -passin pass:"$CERTIFICATE_PASSWORD" -passout pass:temp 2>/dev/null; then echo "โœ… Certificate contains a private key" else echo "โŒ Certificate does NOT contain a private key or wrong password" fi shell: bash - name: Sign with Developer ID run: | echo "๐Ÿ” Signing app with Developer ID certificate..." # Decide which keychain to use if [ "${USE_SYSTEM_CERT:-false}" = "true" ]; then echo "Using system keychain identity" # Get certificate hash instead of name to avoid ambiguity IDENTITY_HASH=$(security find-identity -v -p codesigning | grep "Developer ID Application" | head -1 | awk '{print $2}') echo "Using certificate hash: $IDENTITY_HASH" else # Make sure keychain is unlocked security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" echo "Using custom keychain identity" # Get certificate hash instead of name to avoid ambiguity IDENTITY_HASH=$(security find-identity -v -p codesigning "$KEYCHAIN_PATH" | grep "Developer ID Application" | head -1 | awk '{print $2}') echo "Using certificate hash: $IDENTITY_HASH" fi if [ -z "$IDENTITY_HASH" ]; then echo "โŒ No valid Developer ID Application certificate found" echo "Falling back to ad-hoc signing for testing..." # Use ad-hoc identity as fallback codesign --force --deep --verbose --options runtime --entitlements LuckyWorld.entitlements --sign - --timestamp "$APP_PATH" echo "SIGNED=adhoc" >> "$GITHUB_ENV" else echo "Signing app bundle with Developer ID hash: $IDENTITY_HASH" # Sign the app bundle using the hash codesign --force --deep --verbose --options runtime --entitlements LuckyWorld.entitlements --sign "$IDENTITY_HASH" --timestamp "$APP_PATH" echo "SIGNED=identity" >> "$GITHUB_ENV" fi # Verify signing echo "๐Ÿ” Verifying signature..." codesign -vvv --deep --strict "$APP_PATH" # Check entitlements echo "๐Ÿ” Checking entitlements..." codesign -d --entitlements - "$APP_PATH" shell: bash - name: Notarize App if: success() env: APPLE_ID: ${{ secrets.NOTARY_USER }} APP_PASSWORD: ${{ secrets.NOTARY_PASSWORD }} API_KEY_ID: ${{ secrets.NOTARY_API_KEY_ID }} API_ISSUER_ID: ${{ secrets.NOTARY_API_KEY_ISSUER_ID }} API_KEY_PATH: ${{ secrets.NOTARY_API_KEY_PATH }} run: | echo "๐Ÿ“ค Notarizing app..." # Check if we have API key credentials if [ -n "$API_KEY_ID" ] && [ -n "$API_ISSUER_ID" ] && [ -n "$API_KEY_PATH" ]; then echo "Using App Store Connect API key for notarization..." # Create directory for API key if API_KEY_PATH contains content mkdir -p ~/private_keys # Check if API_KEY_PATH is a path or content if [[ "$API_KEY_PATH" == /* ]] && [ -f "$API_KEY_PATH" ]; then # It's a path to a file echo "Using API key from path: $API_KEY_PATH" cp "$API_KEY_PATH" ~/private_keys/AuthKey_${API_KEY_ID}.p8 else # It contains the key content echo "Using API key from content" echo "$API_KEY_PATH" > ~/private_keys/AuthKey_${API_KEY_ID}.p8 fi # Create zip for notarization ZIP_PATH="TestApp-notarize.zip" ditto -c -k --keepParent "$APP_PATH" "$ZIP_PATH" echo "Submitting for notarization with API key..." xcrun notarytool submit "$ZIP_PATH" \ --key ~/private_keys/AuthKey_${API_KEY_ID}.p8 \ --key-id "$API_KEY_ID" \ --issuer "$API_ISSUER_ID" \ --wait # Staple the notarization ticket echo "Stapling notarization ticket..." xcrun stapler staple "$APP_PATH" # Verify notarization echo "๐Ÿ” Verifying notarization..." spctl --assess --verbose --type exec "$APP_PATH" echo "NOTARIZED=true" >> "$GITHUB_ENV" # Clean up rm -rf ~/private_keys # Fall back to App-specific password if API key not available elif [ -n "$APPLE_ID" ] && [ -n "$APP_PASSWORD" ] && [ -n "$APPLE_TEAM_ID" ]; then echo "Using App-specific password for notarization..." # Create zip for notarization ZIP_PATH="TestApp-notarize.zip" ditto -c -k --keepParent "$APP_PATH" "$ZIP_PATH" echo "Submitting for notarization..." xcrun notarytool submit "$ZIP_PATH" \ --apple-id "$APPLE_ID" \ --password "$APP_PASSWORD" \ --team-id "$APPLE_TEAM_ID" \ --wait # Staple the notarization ticket echo "Stapling notarization ticket..." xcrun stapler staple "$APP_PATH" # Verify notarization echo "๐Ÿ” Verifying notarization..." spctl --assess --verbose --type exec "$APP_PATH" echo "NOTARIZED=true" >> "$GITHUB_ENV" else echo "โš ๏ธ Missing notarization credentials. Skipping notarization." echo "For App Store Connect API key method, set these secrets:" echo " - NOTARY_API_KEY_ID: Your API key ID" echo " - NOTARY_API_KEY_ISSUER_ID: Your API issuer ID" echo " - NOTARY_API_KEY_PATH: Path to or content of your p8 file" echo "" echo "For App-specific password method, set these secrets:" echo " - NOTARY_USER: Your Apple ID (email)" echo " - NOTARY_PASSWORD: Your app-specific password" echo " - APPLE_TEAM_ID: Your Apple Developer team ID" echo "NOTARIZED=false" >> "$GITHUB_ENV" exit 0 fi shell: bash - name: Package Signed App run: | echo "๐Ÿ“ฆ Packaging signed app..." if [ "${NOTARIZED:-false}" == "true" ]; then ZIP_FILE="TestApp-Signed-Notarized.zip" echo "Creating distribution package with notarized app..." else ZIP_FILE="TestApp-Signed.zip" echo "Creating distribution package with signed app..." fi # Create zip package ditto -c -k --keepParent "$APP_PATH" "$ZIP_FILE" echo "โœ… Created package: $ZIP_FILE" shell: bash - name: Upload Artifact uses: actions/upload-artifact@v3 with: name: LuckyWorld-Signed-App path: TestApp-*.zip retention-days: 7 - name: Cleanup if: always() run: | echo "๐Ÿงน Cleaning up..." rm -rf TestApp.app TestApp-*.zip || true security delete-keychain "$KEYCHAIN_PATH" 2>/dev/null || true echo "โœ… Cleanup complete" shell: bash