mirror of
https://github.com/flutter/flutter.git
synced 2026-01-09 07:51:35 +08:00
# flutter_tools: Auto-generate ExportOptions.plist for manual iOS code signing ## Summary Enhance `flutter build ipa` to automatically generate a complete ExportOptions.plist for manual code signing configurations, eliminating the need for users to create and maintain one manually. **Fixes:** #177853 ## Problem When using manual code signing (`CODE_SIGN_STYLE=Manual`) with `flutter build ipa` for Release/Profile builds, the archive succeeds but the export step fails with: ``` error: exportArchive: "Runner.app" requires a provisioning profile with the Push Notifications and Sign in with Apple features. ``` **Root cause:** Flutter generated an incomplete ExportOptions.plist that only included `method` and `uploadBitcode`. When using manual signing, `xcodebuild -exportArchive` requires: - `teamID` (from project's `DEVELOPMENT_TEAM`) - `signingStyle=manual` - `provisioningProfiles` (mapping bundle ID to provisioning profile UUID) Without these, Xcode cannot resolve the provisioning profile for export, even if the profile is correctly configured and contains the required entitlements. ## Solution Enhanced `_createExportPlist()` in `BuildIOSArchiveCommand` to automatically generate a complete ExportOptions.plist when: 1. `CODE_SIGN_STYLE=Manual` is detected for the main app target 2. Build mode is Release or Profile (production builds only) 3. A valid provisioning profile can be located and parsed The generated plist includes: - `method` (app-store, ad-hoc, enterprise, etc. - respects user's `--export-method` flag) - `teamID` (from `DEVELOPMENT_TEAM` build setting) - `signingStyle=manual` - `provisioningProfiles` mapping main app bundle ID to provisioning profile UUID **Fallback behavior:** - If profile lookup fails: silently fall back to simple plist (no regression) - For Automatic signing: unchanged behavior - For Debug builds: unchanged behavior (not App Store export) - For multi-target apps (extensions): falls back to simple plist today (see TODO below) ## Changes ### Core Implementation - Added `ProfileData` class to encapsulate provisioning profile info (UUID and name) - Refactored profile handling into reusable static methods for testability - Enhanced `_createExportPlist()` with manual signing detection and profile lookup - Added `_findProvisioningProfileUuid()` to locate provisioning profiles by specifier - Added `_parseProvisioningProfileInfo()` to decode provisioning profile data once - Added comprehensive trace logging for debugging ### Testing - Created `build_ipa_export_plist_test.dart` with 7 unit tests covering: - Manual signing with profile found → enhanced plist generated ✓ - Automatic signing → simple plist (unchanged behavior) ✓ - Debug builds → simple plist (unchanged behavior) ✓ - Different export methods → respected in generated plist ✓ - Profile lookup failures → fallback to simple plist ✓ - Null codeSignStyle → handled gracefully ✓ - All existing flutter_tools tests continue to pass ## Documentation Added extensive inline comments explaining: - Enhancement conditions and fallback behavior - Provisioning profile search strategy and error handling - Performance and security considerations - Future enhancements (multi-target support) ## Safety & Scope ### What This Fixes - ✅ Manual signing export failures for apps with Push Notifications / Sign in with Apple - ✅ Auto-generates correct plist for **any** entitlements covered by provisioning profile - ✅ Unblocks users from manual `--export-options-plist` workaround - ✅ Improves iOS build UX for enterprise teams doing manual signing ### What This Does NOT Change - ✅ Automatic signing behavior (unchanged) - ✅ Debug builds (unchanged) - ✅ CLI interface (no new flags) - ✅ Ad-hoc / enterprise distributions (unchanged if not using manual signing) ### Known Limitations (Future Enhancements) - Currently only supports single-target apps (main app bundle ID only) - TODO: Multi-target apps with extensions (notification service, widgets, watch, etc.) - each extension target bundle ID may need separate entry in provisioningProfiles dict - TODO: Support for multiple provisioning profiles if app uses multiple signing identities --- ## Pre-merge Checklist - [x] I have signed the CLA - [x] I read the Contributor Guide and Tree Hygiene docs - [x] I linked the issue this fixes (#177853) and explained the failure scenario - [x] I added documentation and trace logs so developers understand what changed - [x] I added comprehensive unit tests for the new behavior - [x] All existing and new tests pass locally (`flutter test packages/flutter_tools`) - [x] Code passes linting (`dart analyze`) ## Safety Notes - [x] This code only runs for manual code signing in `flutter build ipa` for Release/Profile builds - [x] Debug builds and automatic signing behavior are completely unchanged - [x] If no provisioning profile UUID is found, we silently fall back to the old exportOptions.plist logic instead of failing - [x] Added trace-level logging explaining success/fallback scenarios for debugging - [x] No breaking changes - existing workarounds continue to work - [x] Minimal blast radius - only affects manual signing export path ## Verification Users can verify the fix by: 1. Creating an iOS app with `CODE_SIGN_STYLE=Manual`, `DEVELOPMENT_TEAM` set, and Push Notifications entitlements 2. Running `flutter build ipa --release --verbose` 3. Confirming the trace log shows "Generated ExportOptions.plist with teamID, signingStyle=manual, and provisioningProfiles" 4. Verifying the IPA is successfully created without needing `--export-options-plist` --- ## Related Issues - #106612 - Support `flutter build ipa` with manual signing and provisioning profiles - #113977 - Flutter build IPA with --export-options-plist not working --------- Co-authored-by: Elijah Okoroh <okorohelijah@google.com>