feat(shorebird_cli): prepare for Flutter 3.44 libapp.so strip handover#3758
Merged
Conversation
Upstream Flutter PR flutter/flutter#181275 (merged 2026-01-26, first shipped in 3.44) inverted the responsibility for stripping libapp.so. Before 3.44, Flutter stripped the AOT shared library itself and shipped the stripped binary directly. From 3.44 onward, Flutter leaves debug symbols in libapp.so and expects AGP's stripReleaseDebugSymbols task to strip them and produce a matching libapp.so.sym companion in the AAB's BUNDLE-METADATA. flutter_tools adds a post-build verification that fatal errors when the .sym companion is missing. Two Shorebird flows trip the new check on 3.44: 1. Obfuscated release and patch builds pass --extra-gen-snapshot-options= --strip so gen_snapshot pre-strips libapp.so. On 3.44, AGP then has nothing to strip, the .sym companion is never produced, and the build fails with "libapp.so.sym or libapp.so.dbg not present when checking final appbundle for debug symbols." 2. Pre-3.44 Shorebird users (and the e2e fixture) carry a legacy `packaging.jniLibs.keepDebugSymbols.add("**/libapp.so")` line in android/app/build.gradle(.kts). On 3.44 that line blocks AGP from stripping, the .sym companion is not produced, and the build hits the same failure path. Land version-gated behavior in shorebird_cli ahead of the actual 3.44 engine bump so users upgrading their CLI today are pre-loaded with the correct behavior: * Add a libappStrippedByAgpConstraint (>= 3.44) to flutter_version_constraints. * In Releaser.addObfuscationMapArgs, drop --strip for Android+3.44 (other platforms still pre-strip; AGP is not in their pipeline). The .sym in BUNDLE-METADATA is only available to the developer via Play Console and is stripped before APK delivery, so the obfuscation protection in the user-shipped APK remains intact. * In PatchCommand, drop --strip for Android patch builds when the release was built on 3.44, gated on release.flutterRevision so patch gen_snapshot behavior matches the release. * Add a LegacyKeepDebugSymbolsValidator that scans android/app/build.gradle(.kts) for the legacy keepDebugSymbols line on Flutter 3.44 or newer pins and surfaces a warning pointing at the exact file and substring to remove. No-op on older Flutter where the line is still appropriate. The companion Flutter-fork patches (revert of the iOS Mach-O dylib PR and demotion of the libapp.so.sym check from fatal to warning) are still needed as a safety net for users on truly stale build.gradle files; this PR shrinks the population that needs the safety net. Tracked in shorebirdtech/_shorebird#2150.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
bdero
commented
May 12, 2026
| /// On older Flutter versions the line is still appropriate and this validator | ||
| /// is a no-op. | ||
| /// | ||
| /// Tracked in https://github.com/shorebirdtech/_shorebird/issues/2150. |
bdero
commented
May 12, 2026
| /// is a no-op. | ||
| /// | ||
| /// Tracked in https://github.com/shorebirdtech/_shorebird/issues/2150. | ||
| class LegacyKeepDebugSymbolsValidator extends Validator { |
Member
Author
There was a problem hiding this comment.
I believe vanilla Flutter users will also run into this same surprise opaque build failure, and their AI agents will also flail around for an hours investigating. So considering possibly upstreaming something to Flutter doctor.
eseidel
approved these changes
May 13, 2026
|
any timeline for 3.44 support, builds are failing after adding support for built in kotlin |
Contributor
|
We've prepared it. I expect we'll ship it today. |
…lper
Releaser.addObfuscationMapArgs and PatchCommand.createPatch both inlined
the same branching: on Android with Flutter 3.44+, AGP performs the
strip and we must omit --strip from gen_snapshot; everywhere else we
keep --strip. Lift the logic onto ShorebirdFlutter as a single
{platform, flutterRevision}-keyed method so both call sites reduce to a
helper call and the gating policy lives in one place.
Tests for the unresolvable-version edge case move from each call site
to the helper-level group, since the fallback to the constraint's
min-version is a property of the helper rather than its callers.
The non-Android-pipeline releaser tests (ios, ios_framework, linux,
macos, windows) now stub the helper to return true in their --obfuscate
setUps, matching the actual runtime behavior on those platforms.
…docs The shorebirdtech/_shorebird repo is private, so links to its issues read as broken refs to anyone reading the published CLI source.
Flutter 3.44 (flutter/flutter#181539 and related) reorganized iOS USB artifact URLs from ios-usb-dependencies/<artifact>/<hash>/<artifact>.zip to ios-usb-dependencies/arm64_x86_64/<artifact>/<hash>/<artifact>.zip Upstream GCS serves both layouts. The proxy's allowlist only knew the pre-3.44 shape, so any 3.44+ customer's first iOS or Android build 404s during `flutter precache` against download.shorebird.dev. Add the 3.44+ patterns alongside the existing ones for each of the seven iOS USB dependencies. Pre-3.44 customers keep resolving against the old patterns unchanged.
After the strip-gating tests switched from mocking resolveFlutterVersion to mocking shouldPreStripLibappInGenSnapshot, this file no longer constructs Version objects. The unused import was a `dart analyze --fatal-warnings` failure under CI.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Upstream Flutter PR flutter/flutter#181275 (merged 2026-01-26, first shipped in 3.44) inverted the responsibility for stripping
libapp.so. Before 3.44, Flutter stripped the AOT shared library itself. From 3.44 onward, Flutter expects AGP to strip it and emit alibapp.so.symcompanion into the AAB'sBUNDLE-METADATA. flutter_tools adds a post-build verification that fatal errors when the.symcompanion is missing.Two Shorebird flows trip the new check on 3.44:
--extra-gen-snapshot-options=--stripso gen_snapshot pre-stripslibapp.so. On 3.44 AGP then has nothing to strip, no.symis produced, build fails.packaging.jniLibs.keepDebugSymbols.add(\"**/libapp.so\")line inandroid/app/build.gradle(.kts). On 3.44 that blocks AGP from stripping, same failure.This PR lands forward-compatible, version-gated behavior in shorebird_cli so users updating their CLI today are pre-loaded with the right behavior when the actual 3.44 engine bump ships. On older Flutter pins behavior is unchanged.
shorebird_cli changes
libappStrippedByAgpConstraint(>= 3.44) toflutter_version_constraints.dart.ShorebirdFlutter.shouldPreStripLibappInGenSnapshot({platform, flutterRevision}): single helper that owns the strip-gating policy (non-Android always pre-strips; Android pre-strips only below 3.44).Releaser.addObfuscationMapArgs: on Android+3.44, drop--stripso AGP can strip and emit the.sym. Other platforms still pre-strip (AGP isn't in their pipeline). The.symin BUNDLE-METADATA is only visible via Play Console and is stripped before APK delivery, so obfuscation protection in user-shipped APKs is preserved. Method becomesasyncbecause the version check is async.PatchCommand: same gating for Android patch builds, keyed onrelease.flutterRevisionso the patch's gen_snapshot behavior matches the release's.LegacyKeepDebugSymbolsValidator: scansandroid/app/build.gradle(.kts)for the legacykeepDebugSymbolsline on Flutter 3.44+ pins and surfaces a warning pointing at the exact file and substring to remove. Matches both.add(...)and+=forms, single and double quotes. No-op on older Flutter where the line is still appropriate.Doctor.androidCommandValidatorsandDoctor.initAndDoctorValidators.artifact_proxy change
Flutter 3.44 also reorganized iOS USB artifact URLs from
to
(see flutter/flutter#181539 and related). Upstream GCS serves both layouts indefinitely, but the
download.shorebird.devproxy's allowlist only recognized the pre-3.44 shape. Without this change every 3.44+ customer hits404on their first iOS or Android build duringflutter precache(libimobiledevice, ios-deploy, etc.). Adds the sevenarm64_x86_64/variants alongside the existing patterns so customers on either Flutter version resolve.The companion Flutter-fork patches (revert of the iOS Mach-O dylib PR and demotion of the
libapp.so.symcheck from fatal to warning) are still needed as a safety net for users on truly stalebuild.gradlefiles; this PR shrinks the population that needs the safety net.Test plan
dart test test/src/validators/legacy_keep_debug_symbols_validator_test.dart(10 tests)dart test test/src/shorebird_flutter_test.dart(full suite plus 4 new tests forshouldPreStripLibappInGenSnapshot)dart test test/src/commands/release/ test/src/commands/patch/(453 tests)dart test test/src/validators/ test/src/commands/doctor_command_test.dart(103 tests)packages/artifact_proxydart test(11 existing proxy tests still pass)dart analyze --fatal-warnings lib testcleancurl -sI .../arm64_x86_64/libimobiledevice/<hash>/libimobiledevice.zipreturns 302 to storage.googleapis.comcurl -sI .../libimobiledevice/<hash>/libimobiledevice.zip(no arm64_x86_64 segment) still returns 302