diff --git a/apps/readest-app/scripts/release-ios-appstore.sh b/apps/readest-app/scripts/release-ios-appstore.sh index 2b28e0a3..62f42182 100644 --- a/apps/readest-app/scripts/release-ios-appstore.sh +++ b/apps/readest-app/scripts/release-ios-appstore.sh @@ -3,6 +3,10 @@ pnpm tauri ios build --export-method app-store-connect BUNDLE_DIR=src-tauri/gen/apple/build/arm64 IPA_BUNDLE=$BUNDLE_DIR/Readest.ipa +# Guard: the App Store export re-sign must not strip the App Group entitlement +# from the widget / share extensions, or the reading widget ships dead. +bash scripts/verify-ios-appstore-entitlements.sh "$IPA_BUNDLE" + xcrun altool --upload-app --type ios --file $IPA_BUNDLE --apiKey $APPLE_API_KEY --apiIssuer $APPLE_API_ISSUER echo "iOS build uploaded to App Store Connect." diff --git a/apps/readest-app/scripts/verify-ios-appstore-entitlements.sh b/apps/readest-app/scripts/verify-ios-appstore-entitlements.sh new file mode 100755 index 00000000..42169196 --- /dev/null +++ b/apps/readest-app/scripts/verify-ios-appstore-entitlements.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +# Verify the App Store IPA's app extensions carry the App Group entitlement. +# +# group.com.bilingify.readest is how the main app hands the reading-widget +# snapshot (and the share extension its shared state) to its extensions via the +# shared App Group container. Automatic App Store signing re-signs embedded +# extensions during `xcodebuild -exportArchive`; if a target sets +# CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION=YES, that re-sign silently strips +# com.apple.security.application-groups from the extension binary even though +# the provisioning profile and source .entitlements both grant it. The widget +# then reads an empty snapshot and shows only a placeholder book icon, with no +# build error. This guard fails the release before upload so it can't ship +# broken again. +set -euo pipefail + +IPA="${1:-src-tauri/gen/apple/build/arm64/Readest.ipa}" +GROUP="group.com.bilingify.readest" +EXTS=(ReadestWidget ShareExtension) + +if [ ! -f "$IPA" ]; then + echo "verify-ios-appstore-entitlements: IPA not found at $IPA" >&2 + exit 1 +fi + +WORK="$(mktemp -d)" +trap 'rm -rf "$WORK"' EXIT +unzip -q "$IPA" -d "$WORK" +APP="$WORK/Payload/Readest.app" + +fail=0 +for ext in "${EXTS[@]}"; do + appex="$APP/PlugIns/$ext.appex" + if codesign -d --entitlements :- "$appex" 2>/dev/null | grep -q "$GROUP"; then + echo "OK $ext.appex carries $GROUP" + else + echo "FAIL $ext.appex is MISSING $GROUP (App Group access broken)" + fail=1 + fi +done + +if [ "$fail" -ne 0 ]; then + echo "" >&2 + echo "App Group entitlement missing from an extension binary. Do NOT set" >&2 + echo "CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION on extension targets in" >&2 + echo "src-tauri/gen/apple/project.yml." >&2 + exit 1 +fi + +echo "All app extensions carry the App Group entitlement." diff --git a/apps/readest-app/src-tauri/gen/apple/project.yml b/apps/readest-app/src-tauri/gen/apple/project.yml index 04bbd4ac..761d09f7 100644 --- a/apps/readest-app/src-tauri/gen/apple/project.yml +++ b/apps/readest-app/src-tauri/gen/apple/project.yml @@ -153,16 +153,16 @@ targets: PRODUCT_BUNDLE_IDENTIFIER: com.bilingify.readest.ShareExtension DEVELOPMENT_TEAM: J5W48D69VR CODE_SIGN_ENTITLEMENTS: ShareExtension/ShareExtension.entitlements - # Permit Xcode's productPackagingUtility to write the auto-derived - # signing entitlements (application-identifier, team-identifier, - # get-task-allow) back to the source entitlements file at build - # time. Without this, Xcode 16+ refuses the modification it - # itself just performed and the build fails with: - # "Entitlements file ... was modified during the build" - # Safe for this target because the entitlements file is empty — - # the only modifications Xcode performs are the team / app-id - # auto-population every iOS extension target needs anyway. - CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION: YES + # Do NOT set CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION here. This + # entitlements file declares com.apple.security.application-groups + # (group.com.bilingify.readest). With automatic App Store signing, + # that flag lets `xcodebuild -exportArchive` overwrite the extension + # entitlements with a minimal auto-derived set that DROPS the app + # group — silently breaking App Group access in App Store builds + # (dev builds are unaffected). The main app ships the group + # correctly without this flag. If Xcode ever reports "Entitlements + # file was modified during the build", fix it by adding the + # auto-derived keys to the source file, not by re-enabling this flag. SWIFT_VERSION: "5.0" IPHONEOS_DEPLOYMENT_TARGET: 15.0 ARCHS: [arm64] @@ -192,7 +192,10 @@ targets: PRODUCT_BUNDLE_IDENTIFIER: com.bilingify.readest.ReadestWidget DEVELOPMENT_TEAM: J5W48D69VR CODE_SIGN_ENTITLEMENTS: ReadestWidget/ReadestWidget.entitlements - CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION: YES + # CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION is intentionally omitted + # (see ShareExtension): with it, the App Store export re-sign strips + # com.apple.security.application-groups, so the widget reads an empty + # snapshot from the App Group and shows only the placeholder book icon. SWIFT_VERSION: "5.0" IPHONEOS_DEPLOYMENT_TARGET: 15.0 ARCHS: [arm64]