ci(ios): package .app as .ipa before artifact upload#14441
ci(ios): package .app as .ipa before artifact upload#14441farhangnaderi wants to merge 8 commits into
Conversation
GitHub Actions upload-artifact wraps directory inputs in a zip, so uploading QGroundControl.app directly produces a .zip download. Package the .app into a proper IPA (Payload/App.app zipped) first so the artifact is a single file with the correct iOS distribution format.
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds an explicit packaging step to produce an .ipa from the Release build output and updates the existing attest/upload step to publish the .ipa artifact.
Changes:
- Add a “Package IPA” step for Release builds that zips the
.appinto an.ipa. - Switch the attest/upload inputs from
.appto.ipa.
| cd "$APP_DIR" | ||
| mkdir -p Payload | ||
| cp -r "${{ env.PACKAGE }}.app" Payload/ | ||
| zip -r "${{ env.PACKAGE }}.ipa" Payload/ |
| run: | | ||
| APP_DIR="${{ runner.temp }}/build/${{ matrix.build_type }}" | ||
| cd "$APP_DIR" | ||
| mkdir -p Payload | ||
| cp -r "${{ env.PACKAGE }}.app" Payload/ |
Build ResultsPlatform Status
All builds passed. Pre-commit
Pre-commit hooks: 2 passed, 42 failed, 7 skipped. Test Resultslinux-coverage: 88 passed, 0 skipped Code CoverageCoverage: 60.4% No baseline available for comparison Artifact Sizes
Updated: 2026-05-29 19:28:15 UTC • Triggered by: Linux |
MACOSX_BUNDLE_INFO_PLIST was unconditionally set to the macOS plist. The iOS override only set XCODE_ATTRIBUTE_INFOPLIST_FILE, which is Xcode-generator-only. With Ninja, the bundle got the macOS Info.plist (LSMinimumSystemVersion instead of LSRequiresIPhoneOS), causing sideloading tools to reject it as an invalid iOS app.
Codecov Report✅ All modified and coverable lines are covered by tests. ❌ Your project check has failed because the head coverage (26.45%) is below the target coverage (30.00%). You can increase the head coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## master #14441 +/- ##
==========================================
+ Coverage 25.47% 26.45% +0.98%
==========================================
Files 769 767 -2
Lines 65912 66285 +373
Branches 30495 30667 +172
==========================================
+ Hits 16788 17538 +750
+ Misses 37285 36304 -981
- Partials 11839 12443 +604
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 133 files with indirect coverage changes Continue to review full report in Codecov by Sentry.
🚀 New features to boost your workflow:
|
iOS-Info.plist uses Xcode build variables (e.g. $(EXECUTABLE_NAME))
that Xcode substitutes at build time but Ninja leaves as literal strings,
causing sideloading tools to fail finding the executable.
Add iOSBundleInfo.plist.in using CMake-style ${VAR} substitution for
Ninja builds. Xcode generator continues using iOS-Info.plist via
XCODE_ATTRIBUTE_INFOPLIST_FILE unchanged.
With Ninja generator, Xcode's automatic 'Embed Frameworks' build phase doesn't run, so FFmpeg xcframeworks are not copied into the app bundle. The binary's rpath contains the CI runner's absolute Qt path which doesn't exist on device, causing a DYLD crash at launch. Add a POST_BUILD command to copy xcframeworks from Qt's ffmpeg directory into the app bundle's Frameworks/ directory, and set BUILD_RPATH to @executable_path/Frameworks so dyld resolves them correctly on device.
GStreamerMobile is a project-built SHARED framework. With Ninja generator, it is not automatically copied into the app bundle. Add a POST_BUILD command in FindGStreamerMobile to copy it into QGroundControl.app/Frameworks/ on iOS non-Xcode builds.
The previous attempt added add_custom_command in FindGStreamerMobile.cmake, but that runs before add_subdirectory(src) creates the GStreamerMobile target so CMake silently ignored it. Use cmake_language(DEFER DIRECTORY root ...) in Apple.cmake so the post-build copy is registered after all subdirectories are processed and the GStreamerMobile target exists. Use TARGET_FILE_DIR instead of TARGET_BUNDLE_DIR for the framework source path as it reliably resolves to the .framework directory on iOS.
gst_ffmpeg_cfg_init hits a g_assert failure when the GStreamer libav plugin initializes on iOS, aborting the app at launch. iOS has hardware video decoding via applemedia, so libav/FFmpeg software decoding via GStreamer is not needed. Guard GST_PLUGIN_STATIC_DECLARE/REGISTER(libav) with Q_OS_IOS in GStreamer.cc, and remove libav from the iOS GStreamer plugin list in FindQGCGStreamer.cmake so it is not linked into gstreamer_mobile.
applemedia directly interfaces with Apple media APIs that change between iOS versions. On iOS 26 it appears to corrupt the GStreamer element factory registry, causing a SIGSEGV in analyze_new_pad via gst_element_factory_get_metadata with a bad pointer. Remove it to use software decoding path instead.
Description
GitHub Actions upload-artifact wraps directory inputs (like .app bundles) in a zip. This packages the .app as a proper .ipa first so the downloaded artifact has the correct iOS format. Am trying to produce artifacts here from CI to see if it fixes.
Type of Change
Testing
Platforms Tested
Related Issues
By submitting this pull request, I confirm that my contribution is made under the terms of the project's dual license (Apache 2.0 and GPL v3).