From 7b55fee58b825e9136da412f04abd738a7032044 Mon Sep 17 00:00:00 2001 From: Gerald Versluis Date: Wed, 8 Apr 2026 13:49:54 +0200 Subject: [PATCH 1/4] fix: address all 4 Apple validation errors for Mac Catalyst 1. Bundle ID: use nl.versluis.polypilot (Apple says it was already registered as this, cannot change to maccatalyst. prefix) 2. LSApplicationCategoryType: add public.app-category.developer-tools to Info.plist (required for Mac App Store) 3. Copilot binary signing: re-sign the bundled copilot CLI with the same identity and sandbox entitlements after dotnet publish 4. Copilot binary sandbox: included via the entitlements used in the re-sign step Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/release-apps.yml | 18 +++++++++++++++++- PolyPilot/Platforms/MacCatalyst/Info.plist | 4 ++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release-apps.yml b/.github/workflows/release-apps.yml index 2d44b28302..ea4ea03207 100644 --- a/.github/workflows/release-apps.yml +++ b/.github/workflows/release-apps.yml @@ -218,11 +218,13 @@ jobs: echo "Installed Mac Catalyst profile with UUID: $PROFILE_UUID" - name: Publish Mac Catalyst PKG + env: + CODESIGN_KEY: ${{ secrets.IOS_CODESIGN_KEY }} run: | dotnet publish ${{ env.MAUI_PROJECT_PATH }} \ -f net10.0-maccatalyst \ -c Release \ - -p:ApplicationId=maccatalyst.nl.versluis.polypilot \ + -p:ApplicationId=nl.versluis.polypilot \ -p:CodesignKey="${{ secrets.IOS_CODESIGN_KEY }}" \ -p:CodesignProvision="${{ secrets.MACCATALYST_PROVISIONING_PROFILE_NAME }}" \ -p:CodesignEntitlements=Platforms/MacCatalyst/Entitlements.AppStore.plist \ @@ -233,6 +235,20 @@ jobs: -p:ApplicationDisplayVersion='${{ env.APP_VERSION }}' \ -p:ApplicationVersion='${{ steps.build.outputs.number }}' + # Re-sign the bundled copilot CLI binary with the same identity and entitlements + APP_PATH=$(find PolyPilot/bin/Release/net10.0-maccatalyst -name "PolyPilot.app" -type d | head -1) + COPILOT_BIN="$APP_PATH/Contents/MonoBundle/copilot" + if [ -f "$COPILOT_BIN" ]; then + echo "Re-signing bundled copilot binary..." + codesign --force --sign "$CODESIGN_KEY" \ + --entitlements PolyPilot/Platforms/MacCatalyst/Entitlements.AppStore.plist \ + --options runtime \ + "$COPILOT_BIN" + echo "Copilot binary signed successfully" + else + echo "Warning: copilot binary not found at $COPILOT_BIN" + fi + - name: Find PKG file id: find_pkg run: | diff --git a/PolyPilot/Platforms/MacCatalyst/Info.plist b/PolyPilot/Platforms/MacCatalyst/Info.plist index ecac4e9861..325cf53ebe 100644 --- a/PolyPilot/Platforms/MacCatalyst/Info.plist +++ b/PolyPilot/Platforms/MacCatalyst/Info.plist @@ -9,8 +9,8 @@ - - + LSApplicationCategoryType + public.app-category.developer-tools UIDeviceFamily 2 From e9b117d7ec9af223bbb7ede33c439c103da15cfd Mon Sep 17 00:00:00 2001 From: Gerald Versluis Date: Wed, 8 Apr 2026 13:55:23 +0200 Subject: [PATCH 2/4] fix: keep maccatalyst prefix for build, use base bundle-id for upload Build needs maccatalyst.nl.versluis.polypilot to match the provisioning profile. Apple's upload validation needs the base bundle-id nl.versluis.polypilot to match the App Store Connect record. Use --bundle-id override in altool --upload-package. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/release-apps.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-apps.yml b/.github/workflows/release-apps.yml index ea4ea03207..bfb3e6393a 100644 --- a/.github/workflows/release-apps.yml +++ b/.github/workflows/release-apps.yml @@ -224,7 +224,7 @@ jobs: dotnet publish ${{ env.MAUI_PROJECT_PATH }} \ -f net10.0-maccatalyst \ -c Release \ - -p:ApplicationId=nl.versluis.polypilot \ + -p:ApplicationId=maccatalyst.nl.versluis.polypilot \ -p:CodesignKey="${{ secrets.IOS_CODESIGN_KEY }}" \ -p:CodesignProvision="${{ secrets.MACCATALYST_PROVISIONING_PROFILE_NAME }}" \ -p:CodesignEntitlements=Platforms/MacCatalyst/Entitlements.AppStore.plist \ @@ -304,7 +304,7 @@ jobs: xcrun altool --upload-package "$PKG_PATH" \ -t macos \ --apple-id "6759370598" \ - --bundle-id "maccatalyst.nl.versluis.polypilot" \ + --bundle-id "nl.versluis.polypilot" \ --bundle-version "$BUILD_NUMBER" \ --bundle-short-version-string "$APP_VERSION" \ --apiKey "$APPSTORE_KEY_ID" \ From e485e3460f05ed7bbcd680c91479fecf18ca425e Mon Sep 17 00:00:00 2001 From: Gerald Versluis Date: Wed, 8 Apr 2026 14:03:25 +0200 Subject: [PATCH 3/4] fix: split build/sign/package into separate steps CreatePackage=true seals the .pkg before the copilot binary can be re-signed. Split into 3 steps: produces .app 2. Re-sign copilot binary + dylibs + frameworks + .app (inside-out) creates .pkg Follows the same inside-out signing pattern as build.yml (Homebrew). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/release-apps.yml | 63 +++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 10 deletions(-) diff --git a/.github/workflows/release-apps.yml b/.github/workflows/release-apps.yml index bfb3e6393a..70f38651d8 100644 --- a/.github/workflows/release-apps.yml +++ b/.github/workflows/release-apps.yml @@ -217,9 +217,7 @@ jobs: cp "$RUNNER_TEMP/maccatalyst.provisionprofile" "$PP_PATH/$PROFILE_UUID.provisionprofile" echo "Installed Mac Catalyst profile with UUID: $PROFILE_UUID" - - name: Publish Mac Catalyst PKG - env: - CODESIGN_KEY: ${{ secrets.IOS_CODESIGN_KEY }} + - name: Publish Mac Catalyst App run: | dotnet publish ${{ env.MAUI_PROJECT_PATH }} \ -f net10.0-maccatalyst \ @@ -228,27 +226,72 @@ jobs: -p:CodesignKey="${{ secrets.IOS_CODESIGN_KEY }}" \ -p:CodesignProvision="${{ secrets.MACCATALYST_PROVISIONING_PROFILE_NAME }}" \ -p:CodesignEntitlements=Platforms/MacCatalyst/Entitlements.AppStore.plist \ - -p:CreatePackage=true \ + -p:CreatePackage=false \ -p:EnableCodeSigning=true \ - -p:EnablePackageSigning=true \ - -p:PackageSigningKey="${{ secrets.MACCATALYST_INSTALLER_SIGNING_KEY }}" \ -p:ApplicationDisplayVersion='${{ env.APP_VERSION }}' \ -p:ApplicationVersion='${{ steps.build.outputs.number }}' - # Re-sign the bundled copilot CLI binary with the same identity and entitlements + - name: Re-sign app with copilot binary + env: + CODESIGN_KEY: ${{ secrets.IOS_CODESIGN_KEY }} + run: | APP_PATH=$(find PolyPilot/bin/Release/net10.0-maccatalyst -name "PolyPilot.app" -type d | head -1) + echo "App path: $APP_PATH" + + # Sign the copilot CLI binary (inside-out: nested content first) COPILOT_BIN="$APP_PATH/Contents/MonoBundle/copilot" if [ -f "$COPILOT_BIN" ]; then - echo "Re-signing bundled copilot binary..." + echo "Signing bundled copilot binary..." codesign --force --sign "$CODESIGN_KEY" \ --entitlements PolyPilot/Platforms/MacCatalyst/Entitlements.AppStore.plist \ - --options runtime \ + --options runtime --timestamp \ "$COPILOT_BIN" - echo "Copilot binary signed successfully" else echo "Warning: copilot binary not found at $COPILOT_BIN" fi + # Re-sign all dylibs (inside-out) + find "$APP_PATH" -type f \( -name "*.dylib" -o -name "*.so" \) | while read f; do + echo " Signing: $f" + codesign --force --options runtime --timestamp \ + --sign "$CODESIGN_KEY" "$f" + done + + # Re-sign frameworks + find "$APP_PATH" -type d -name "*.framework" | while read f; do + echo " Signing: $f" + codesign --force --options runtime --timestamp \ + --sign "$CODESIGN_KEY" "$f" + done + + # Re-sign the top-level app bundle + codesign --force --sign "$CODESIGN_KEY" \ + --entitlements PolyPilot/Platforms/MacCatalyst/Entitlements.AppStore.plist \ + --options runtime --timestamp \ + "$APP_PATH" + + echo "=== Verify signature ===" + codesign -dvv "$APP_PATH" 2>&1 | head -20 + + - name: Create and sign PKG + env: + PKG_SIGN_KEY: ${{ secrets.MACCATALYST_INSTALLER_SIGNING_KEY }} + run: | + APP_PATH=$(find PolyPilot/bin/Release/net10.0-maccatalyst -name "PolyPilot.app" -type d | head -1) + PKG_DIR="PolyPilot/bin/Release/net10.0-maccatalyst" + + # Create component package + productbuild --component "$APP_PATH" /Applications \ + "$PKG_DIR/PolyPilot-unsigned.pkg" + + # Sign the package with Mac Installer certificate + productsign --sign "$PKG_SIGN_KEY" \ + "$PKG_DIR/PolyPilot-unsigned.pkg" \ + "$PKG_DIR/PolyPilot-${{ env.APP_VERSION }}.pkg" + + rm "$PKG_DIR/PolyPilot-unsigned.pkg" + echo "Created signed PKG" + - name: Find PKG file id: find_pkg run: | From 30a2bc949d071ec24a35def46e9e9c0b0a3497df Mon Sep 17 00:00:00 2001 From: Gerald Versluis Date: Wed, 8 Apr 2026 14:21:45 +0200 Subject: [PATCH 4/4] fix: patch CFBundleIdentifier and copilot entitlements for App Store validation Address all 4 Apple validation errors: 1. Bundle ID mismatch: Build with maccatalyst. prefix (for provisioning profile match), then patch CFBundleIdentifier back to base ID before signing. App Store Connect expects the base bundle ID, not the maccatalyst-prefixed one. 2. Copilot binary signing: Use minimal Entitlements.Helper.plist with app-sandbox + inherit (standard pattern for helper executables in sandboxed apps) instead of the full app entitlements. 3. Missing LSApplicationCategoryType: Already added in previous commit. 4. Copilot sandbox: Covered by the new helper entitlements file. Added --deep --strict signature verification step. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/release-apps.yml | 33 ++++++++++++++++--- .../MacCatalyst/Entitlements.Helper.plist | 13 ++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 PolyPilot/Platforms/MacCatalyst/Entitlements.Helper.plist diff --git a/.github/workflows/release-apps.yml b/.github/workflows/release-apps.yml index 70f38651d8..a4acee2845 100644 --- a/.github/workflows/release-apps.yml +++ b/.github/workflows/release-apps.yml @@ -231,19 +231,40 @@ jobs: -p:ApplicationDisplayVersion='${{ env.APP_VERSION }}' \ -p:ApplicationVersion='${{ steps.build.outputs.number }}' - - name: Re-sign app with copilot binary + - name: Patch bundle identifier for App Store + run: | + APP_PATH=$(find PolyPilot/bin/Release/net10.0-maccatalyst -name "PolyPilot.app" -type d | head -1) + echo "App path: $APP_PATH" + + # MAUI builds with maccatalyst.nl.versluis.polypilot to match the provisioning + # profile, but App Store Connect expects the base bundle ID without the prefix. + # Apple's tooling understands the maccatalyst. prefix convention in profiles, + # but the binary's CFBundleIdentifier must be the base ID. + PLIST="$APP_PATH/Contents/Info.plist" + CURRENT_ID=$(/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "$PLIST") + echo "Current CFBundleIdentifier: $CURRENT_ID" + + if [[ "$CURRENT_ID" == maccatalyst.* ]]; then + BASE_ID="${CURRENT_ID#maccatalyst.}" + /usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $BASE_ID" "$PLIST" + echo "Patched CFBundleIdentifier to: $BASE_ID" + else + echo "CFBundleIdentifier already uses base ID: $CURRENT_ID" + fi + + - name: Re-sign app bundle (inside-out) env: CODESIGN_KEY: ${{ secrets.IOS_CODESIGN_KEY }} run: | APP_PATH=$(find PolyPilot/bin/Release/net10.0-maccatalyst -name "PolyPilot.app" -type d | head -1) echo "App path: $APP_PATH" - # Sign the copilot CLI binary (inside-out: nested content first) + # Sign the copilot CLI binary with minimal helper entitlements (sandbox + inherit) COPILOT_BIN="$APP_PATH/Contents/MonoBundle/copilot" if [ -f "$COPILOT_BIN" ]; then echo "Signing bundled copilot binary..." codesign --force --sign "$CODESIGN_KEY" \ - --entitlements PolyPilot/Platforms/MacCatalyst/Entitlements.AppStore.plist \ + --entitlements PolyPilot/Platforms/MacCatalyst/Entitlements.Helper.plist \ --options runtime --timestamp \ "$COPILOT_BIN" else @@ -264,14 +285,18 @@ jobs: --sign "$CODESIGN_KEY" "$f" done - # Re-sign the top-level app bundle + # Re-sign the top-level app bundle with full app entitlements codesign --force --sign "$CODESIGN_KEY" \ --entitlements PolyPilot/Platforms/MacCatalyst/Entitlements.AppStore.plist \ --options runtime --timestamp \ "$APP_PATH" echo "=== Verify signature ===" + codesign --verify --deep --strict "$APP_PATH" 2>&1 + echo "=== Signature details ===" codesign -dvv "$APP_PATH" 2>&1 | head -20 + echo "=== Verify copilot binary ===" + codesign -dvv "$COPILOT_BIN" 2>&1 | head -10 - name: Create and sign PKG env: diff --git a/PolyPilot/Platforms/MacCatalyst/Entitlements.Helper.plist b/PolyPilot/Platforms/MacCatalyst/Entitlements.Helper.plist new file mode 100644 index 0000000000..0bbc9f1701 --- /dev/null +++ b/PolyPilot/Platforms/MacCatalyst/Entitlements.Helper.plist @@ -0,0 +1,13 @@ + + + + + + com.apple.security.app-sandbox + + com.apple.security.inherit + + +