Skip to content

test: complete per-package e2e migration — all 13 products, secrets-injected configs - #18542

Merged
Lyokone merged 37 commits into
ci/p2-migrate-auth-db-functionsfrom
ci/p2-endgame
Aug 6, 2026
Merged

test: complete per-package e2e migration — all 13 products, secrets-injected configs#18542
Lyokone merged 37 commits into
ci/p2-migrate-auth-db-functionsfrom
ci/p2-endgame

Conversation

@Lyokone

@Lyokone Lyokone commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Final layer of stack #18536. All remaining suites move to per-package, path-filtered, platform-scoped workflows; live-service products run at PR time only for changes to their own package (or core), injected from the new TESTS_E2E_* secrets with fork guards; app identity unified on the registered io.flutter.plugins.firebase.tests; tests/ becomes the coexistence smoke app; all_plugins restructured. Details in the commit message.

@gemini-code-assist

Copy link
Copy Markdown
Contributor
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Completes the per-product e2e architecture: the nine live-service
products (ai, analytics, app_check, app_installations, crashlytics,
messaging, ml_model_downloader, performance, remote_config) move into
their example apps with path-filtered, platform-scoped workflows - a
messaging PR gets live messaging signal at PR time; unrelated PRs never
pay for it. The nightly still runs everything and feeds the dashboard.

Live backends validate app identity, so all nine examples adopt the
mega test app's registered Firebase identity
(io.flutter.plugins.firebase.tests on Android and Apple; namespaces and
code packages untouched). Config values are injected from the
TESTS_E2E_* repository secrets (org direction: no Firebase configs
committed, public-repo abuse) with fail-fast guards; secret-dependent
jobs skip on fork/dependabot PRs. The generator gained a plist-only
mode so a dummy can never clobber injected options; example
lib/firebase_options.dart entries stay listed for melos analyze-ci.

iOS runs SPM for eight of nine (crashlytics stays CocoaPods: its build
phases invoke ${PODS_ROOT}/FirebaseCrashlytics/run and upload-symbols).
Platform jobs exist only where the suite has an implementation; windows
coverage restored for app_check and remote_config.

tests/ is now purely the all-plugins coexistence smoke app (core suite
only); web-app-check job and the APP_CHECK_E2E machinery retired.
all_plugins: swift-integration split into parallel ios/macos jobs;
pub_dry_run, pub_get_check and license checks folded into analyze.

Branch protection heads-up: gone - pub_dry_run, pub_get_check,
check-files-license-headers, swift-integration, '<platform>
(core_misc)'; new - swift-integration-{ios,macos}, '<platform> (core)',
and the thirteen per-package e2e-* workflows.
@Lyokone Lyokone changed the title ci: nightly-only live-service e2e, parallel swift-integration, leaner all_plugins test: complete per-package e2e migration — all 13 products, secrets-injected configs Aug 5, 2026
@wiz-9635d3485b

wiz-9635d3485b Bot commented Aug 5, 2026

Copy link
Copy Markdown

Wiz Scan Summary

Scanner Findings
Vulnerability Finding Vulnerabilities -
Data Finding Sensitive Data -
Secret Finding Secrets -
IaC Misconfiguration IaC Misconfigurations 6 Low
SAST Finding SAST Findings -
Software Management Finding Software Management Findings -
Total 6 Low

View scan details in Wiz

To detect these findings earlier in the dev lifecycle, try the Wiz Code extension for VS Code, JetBrains, or Visual Studio.

@Lyokone
Lyokone marked this pull request as ready for review August 5, 2026 13:09
Lyokone added 23 commits August 5, 2026 15:19
On headless ubuntu runners, '-d chrome' makes the flutter tool launch
Chrome itself, which fails without a display server ('Missing X server
or $DISPLAY') - and flutter drive then EXITS 0, so every ubuntu web job
since the runner migration could report green in ~30s having run zero
tests. Spotted by Guillaume in a 'passing' job's log.

- default device becomes web-server: the browser is launched through
  chromedriver (headless-capable), the documented setup for web
  integration tests
- the script now requires positive evidence ('All tests passed') before
  accepting exit 0; a run with neither that nor test failures is an
  infrastructure failure and retries
The per-package e2e workflows build these example apps' iOS/macOS
targets for the first time; several shipped with build configs that
could never have compiled:

- File.exists? (removed in Ruby 3.2) in two macos Podfiles
- platform :osx below firebase_core's 10.15 minimum in seven macos
  Podfiles (only two had surfaced in CI yet)
- signing configs demanding provisioning profiles (messaging macos,
  and the same latent combination in ml_model_downloader) - project
  ad-hoc signing now applies, matching the healthy storage example
- Podfiles declaring a RunnerTests target their Xcode project does not
  have (app_check, both platforms)
- ml_model_downloader iOS project-level deployment target 12.0 -> 15.0

All 21 live-tier Podfiles pass ruby -c; edited pbxproj files pass
plutil -lint; repo-wide grep for removed Ruby APIs is clean.
- cloud_functions macos: the Podfile was the pre-1.20 hand-rolled
  template (parse_KV_file era) - pods installed but Swift module
  resolution failed; replaced with the modern generic template
- firebase_auth macos: same provisioning-profile signing rot as
  messaging (auth migrated in the earlier layer, outside the live-tier
  sweep's scope); project-level ad-hoc signing now applies
- crashlytics ios: GoogleService-Info.plist was listed in the SOURCES
  build phase - Xcode tried to compile a plist, producing 'Unexpected
  duplicate tasks'; removed from Sources (it belongs in no phase here,
  the Dart-injected options are what's read at runtime)
… e2e-smoke

cloud_firestore was the last product riding the shared platform
matrices (historical: its example predates the per-package
architecture) - a change to any package still built firestore e2e on
four platforms. It now has e2e_tests_firestore.yaml on the standard
template: path-filtered, platform-scoped, SPM on iOS, its windows job
absorbed from windows.yaml.

With firestore out, the five platform workflows contained only the
coexistence smoke leg; they collapse into e2e_tests_smoke.yaml
(android incl. agp9-compatibility, ios, macos, web, web-wasm, windows)
with the broad trigger the smoke deliberately keeps: its job is
building all plugins into one binary on any change.

android.yaml, ios.yaml, macos.yaml, web.yaml, windows.yaml deleted.
End state: 14 per-package e2e workflows + smoke + all_plugins +
nightly - one template everywhere.

Branch protection: '<platform> (core|firestore)' and windows-firestore
disappear; select the new per-workflow android/ios/macos/web/web-wasm/
windows checks qualified by workflow (e2e-smoke / e2e-firestore).
flutter drive on web printed only 'All tests passed.' because the
binding's per-test results map never crosses the wire - only
reportData does (flutter/packages has the same blind spot; it relies
on exit codes alone, and flutter test on web is still blocked by
flutter/flutter#66264). A tearDownAll hook now copies results into
reportData (registered after the binding's own, so LIFO runs it
first), and every test_driver prints one line per test plus a summary.

A run reporting zero tests prints the '[E]' marker and exits 1 - the
strongest guard yet against the browser-never-launched false-green
class: positive per-test evidence instead of trusting a banner.
Windows flutter drive gets the same summaries via the shared driver.
'if run_tests; then exit 0; fi' followed by exit_code=$? never worked:
a completed if-statement with a false condition and no else sets $? to
0 (POSIX), so every failure path collapsed to exit 0. Failures only
ever propagated when bash's errexit happened to kill the script first,
which is why the bug survived: bash 5 crashed loudly where bash 3.2
sailed through. Surfaced by a job that printed the [E] infrastructure
marker and still went green under emulators:exec.
Local verification of the web reporting (auth example, web-server +
chromedriver 150 against the emulator suite) exposed that
binding.results only receives testWidgets entries - runTest is the
widget-test path - so the ~97% of FlutterFire e2e tests declared with
plain test() reported nothing and the summary read '0 total' for a
92-test suite.

The helper now records every executed test from a top-level setUp via
package:test_api/hooks.dart's TestHandle (public API; test_api: any
added to dev_dependencies, resolving to flutter_test's pin - lockfile
unchanged), and attributes failures from binding.results with a
suffix-tolerant match (results keys are bare descriptions, TestHandle
gives group-prefixed names - an exact lookup could never match).

Verified locally end to end: 34 executed web tests reported by name
('34 passed, 0 failed, 34 total'; the rest of the suite is
platform-skipped on web and skipped tests never enter setUp), exit
codes propagate through the retry script and emulators:exec in both
directions.

Known limit: a failed plain test() still shows a per-test checkmark
(no public per-test result API) - the run itself fails via
allTestsPassed, so this cannot go green.
- crashlytics: port firebase_crashlytics_collection_enabled=false from
  the mega-app manifest (the only test-observable manifest config found
  in a sweep of all 14 suites - the test asserts it)
- messaging: desugar_jdk_libs 2.1.4 (flutter_local_notifications 21
  requires it; the mega-app never carried that dependency)
- storage, auth: AGP 8.9.1 / Kotlin 2.1.0 / Gradle 8.12 (transitive
  androidx now requires AGP >= 8.9.1; aligned with the firebase_ai
  example). Both examples build a debug APK locally with this trio.
FirebaseDatabase resolves refFromURL() mismatches only when the app
options carry a databaseURL; without one the guard is skipped (breaking
the mismatch assertion on android) and - worse - the database example's
group bodies touch FirebaseDatabase.instance at declaration time, so on
web the whole registration died synchronously and the run reported
'all tests passed' with zero tests. Verified locally on web-server:
69 passed, 0 failed, 69 total.
The web compiler roots the app at the entrypoint's directory, so the
'../report_test_results.dart' import resolved to a file outside the
compilation root and failed only on web (pipeline's analysis_options
excludes integration_test, which is why analysis missed it). Verified
locally: flutter build web compiles.
The 14 per-package workflows were ~95% identical and already drifting
(a duplicate-if bug had to be back-ported once). They are now ~55-line
callers of reusable_e2e_tests.yaml, which owns the changes/android/ios/
macos/web/web-wasm/windows job set behind typed inputs (platform
toggles, ios-spm, native-config-args, inject-config-secrets as the
live-vs-emulator tier switch, wasm envs). 7283 -> 1922 lines (-74%).
Every caller was mechanically asserted against an inventory extracted
from its pre-refactor file; actionlint validates the caller/reusable
contracts. fdc, pipeline and smoke stay bespoke.

Branch protection: per-product check names become 'e2e / android' etc.
- the web compiler roots the app at the entrypoint's directory, so
  core_shard_test.dart's '../' imports failed only on web (same class
  as the pipeline helper fix); it moves from shards/ to the
  integration_test root. Verified locally on web-server: 6/6 core
  tests pass with per-test reporting
- android e2e jobs get 35 minutes: PR runs always build cold (caches
  save on main only), and cold AVD creation plus a Gradle build did
  not fit 25 - functions and app_installations hit the cap exactly
Half the matrix pinned 3.41.9 while the other half floated stable, so
identical code built against different SDKs depending on the job - and
the iOS e2e failures reproduce only under the pin (everything passes
locally on stable). One channel, no version drift.
…plist phase

- the dummy API key now satisfies FIRInstallations' validation (39
  chars, 'A' prefix, base64url): firebase_core's iOS plugin registrant
  natively configures FIRApp whenever a GoogleService-Info.plist is
  bundled, before any Dart code, and an invalid key is an ObjC
  exception at launch - which flutter test only ever reports as
  'WebSocketChannelException: Connection refused'
- messaging example: CocoaPods-layout header import behind
  __has_include with @import fallback, and hard Pods xcconfig includes
  become optional (#include?) so the ephemeral SPM CI build compiles
  while CocoaPods keeps working for users; auth had one hard include
- crashlytics example: GoogleService-Info.plist joins the Resources
  build phase properly (the earlier fix removed it from Sources where
  it was being compiled; the Crashlytics run script needs it bundled)
- reusable_e2e_tests.yaml: every input consumed inside a run: script is
  now routed through job-level env vars so values are data, never
  shell/template code - resolves all 31 zizmor template-injection
  findings on the new file (inputs come from our own callers today, but
  the scanner's rule is the right hardening posture)
- all_plugins analyze: 'pub publish --dry-run' refuses a dirty tree,
  and melos bootstrap (needed by the analysis steps it now shares a job
  with) syncs versions into Package.swift/Constants.swift; the
  committed state is restored before validating - the standalone job
  this was folded from got that for free from its fresh checkout
On pull_request/push the caller is not itself invoked via workflow_call,
so the inputs context is empty and ${{ inputs.nightly_test_mode }}
renders '' - which fails the boolean type check when handed to the
reusable workflow ('Unexpected value'). '== true' yields a real boolean
in every event context.
… of SPM

Two failures from the same run:

- the plist-only generator mode parses the example's firebase_options
  for the Apple values; on the live tier that file is the INJECTED
  config, which declares 'static const FirebaseOptions ios = ...'
  (typed) where the generator's own template writes the untyped form -
  the parser now accepts both. Verified locally: injecting the real
  config shape and running --live-tier-plist=messaging produces a
  lint-clean plist carrying the injected app id. This was 21 of the 27
  failures (every live-tier ios/macos job died at config generation).

- recent stable Flutter enables Swift Package Manager by default,
  silently turning the intended-CocoaPods builds hybrid: the smoke
  macOS build then trips over firebase_messaging's shared-darwin SPM
  package resolving an ios/ resources path that does not exist in the
  ephemeral copy, and smoke iOS trips SPM manifest version validation.
  CocoaPods-by-design jobs (template macos, template ios with
  ios-spm: false, smoke ios/macos) now opt out explicitly; the
  dedicated SPM jobs keep enabling it explicitly. The messaging
  shared-darwin SPM resolution issue under hybrid mode deserves its
  own investigation.
The mega test app's manifest carried usesCleartextTraffic=true; the
example apps do not, so suites that reach the emulators over plain
HTTP at 10.0.2.2 are blocked by Android's cleartext policy - auth
failed every stream test with 'Cleartext HTTP traffic to 10.0.2.2 not
permitted' and storage's setUpAll upload surfaced as
retry-limit-exceeded. cloud_functions' example already carried the
attribute; database/firestore only survive because their plugins map
the emulator through the native SDK. Applied to all four missing
emulator-tier examples.
The template's tier switch coupled 'where config comes from' with
'whether emulators run'. firebase_auth broke that assumption: its tests
run against the Auth emulator, but validatePassword() calls the live
password-policy REST API, which rejects placeholder credentials - the
mega-app only ever passed because it ran on real committed config.
New use-firebase-emulators input (default true, live tier passes
false); auth now injects real config from secrets while keeping the
emulator suite, and its android applicationId joins the registered
mega-app identity (the injected google-services.json has no
auth.example client). nightly passes the secrets accordingly.

Also:
- iOS E2E steps get the macOS-style exit laundering: flutter test on
  simulators can exit 1 with every test passed (auth ios: '34 passed,
  15 skipped' then exit 1), same class macos.yaml has laundered for
  years
- performance: ios-spm off - its Xcode project predates the format
  Flutter's SPM integration can parse ('Unable to get Xcode project
  information')
- app_installations: getToken skipped on Android with a tracking TODO -
  it deadlocks to the 5-minute timeout reproducibly since the suite
  became a standalone app (the heartbeat-init race its setUpAll delay
  guards)
…ormance Swift config; committed plists win

Four fixes for the run's remaining failures:

- the iOS exit-laundering wrapper captured flutter test via command
  substitution; on iOS the tool spawns simulator log streamers that
  inherit stdout and never close it, so the capture never saw EOF and
  all seven products' iOS E2E steps hung silently to the 20-minute cap
  (macOS spawns no such children, which is why its wrapper worked).
  Output now goes to a file - inherited file descriptors block nothing

- generate-dummy-firebase-configs.dart no longer overwrites committed
  GoogleService-Info.plist files: crashlytics, app_installations and
  messaging check in real plists for their registered apps, and the
  native SDKs configure from the bundle before Dart runs - clobbering
  crashlytics' with a placeholder is the prime suspect for its silent
  launch hang (local repro blocked: CocoaPods on this machine is in
  the broken-Ruby state flutter doctor reports)

- pipeline ios builds with CocoaPods: under stable's SPM integration
  the ephemeral package layout did not materialise for pipeline_example
  ('the folder firebase_core does not exist') - cloud_firestore's iOS
  SPM coverage lives in e2e-firestore, which passes

- performance example: SWIFT_VERSION and
  ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES added to its objectVersion-46
  project (the swiftCompatibility56 link failure); scaffold
  regeneration is the queued follow-up

Not addressed: two AVD-boot hangs (storage, app_installations android)
- the pre-existing emulator infra flake, third sighting, tracked for
the flake budget rather than papered over.
- Flutter's experimental SPM integration intermittently resolves a
  symlinked plugin package at its real path, where the ../firebase_core
  sibling does not exist ('Could not resolve package dependencies') -
  functions ios hit it after passing twice. The template's iOS build
  now retries once, gated on exactly that error string.

- the performance example's iOS scaffold was the repo's last
  objectVersion-46 fossil: Flutter's SPM integration cannot parse it
  and its missing Swift configuration broke CocoaPods linking too
  (swiftCompatibility56) - shimming failed, so it is regenerated from
  the current flutter create template: SPM-only (no Podfile), Swift
  AppDelegate, deployment target 15.0 (Firebase minimum; template
  ships 13.0), bundle identity io.flutter.plugins.firebase.tests
  preserved, firebase_app_id_file.json kept. The ios-spm:false
  workaround is gone - it rejoins SPM coverage. Verified locally:
  builds under SPM in 40s. First concrete slice of the planned
  SPM-only scaffold regeneration.
The Apple exit-laundering checked for the substring 'tests passed',
which '0 tests passed, 1 failed.' happily contains - a genuinely
failing iOS job (app failed to even load the suite) was published as
green. Every wrapper now parses the runner's own tally numerically:
failed > 0 or passed == 0 is a failure, no exceptions; a nonzero
flutter exit with a clean tally (the simulator foreground quirk) is
tolerated and says so.

The zero-output startup hang (flutter test wedging before its first
line, seen across android and ios on loaded runners) is bounded by a
15-minute perl alarm and retried once, keyed on the hang's signature:
no tally at all. Android's exec one-liners stay honest reds for now -
their retry needs the same restructuring and follows separately.
…ate license ignore

- the tally fix exposed five macOS jobs failing on 'The log reader
  stopped unexpectedly' -> 'Unable to start the app on the device' -
  the known intermittent Apple launch race, previously laundered green.
  The wrappers now retry once on that signature too (it produces a
  tally, so the no-tally hang retry never caught it)

- analysis runs through flutter_plugin_tools (the flutter/packages
  driver already powering the format job), with a custom-analysis
  config listing every package since FlutterFire manages options
  centrally; the tests/ app is analyzed alongside. Verified locally.
  publish-check was evaluated and rejected: it errors on
  already-published versions and requires AUTHORS files, neither of
  which fits this repo's release model - the pub dry-run stays on melos

- the regenerated performance scaffold's SceneDelegate.swift (new in
  the modern Flutter template) joins the license-header ignore list
  next to AppDelegate/MainFlutterWindow. Verified locally with
  addlicense
Lyokone added 13 commits August 5, 2026 20:48
The example built as 'Firebase Cloud Messaging Example.app'; flutter's
integration_test attach machinery expects the standard Runner product
and reported 'Unable to start the app on the device' - the app itself
launches fine by hand (verified locally: manual simctl install+launch
succeeds, no crash report). PRODUCT_NAME returns to $(TARGET_NAME)
like every other example.
…launch fixes

- every example entrypoint guards Firebase.initializeApp with
  Firebase.apps.isEmpty: the plugin registrant natively configures
  [DEFAULT] from a bundled GoogleService-Info.plist before Dart runs,
  and the explicit initializeApp then threw [core/duplicate-app] -
  confirmed in CI for app_installations and crashlytics on macOS
  (their committed plists carry the same registered apps, so the
  native-configured instance is the right one to keep)

- messaging macOS: PRODUCT_NAME back to $(TARGET_NAME), same
  attach-machinery fix as iOS

- iOS SPM builds get a --config-only pre-warm so the ephemeral package
  layout exists before the first real resolution (the intermittent
  'the folder firebase_core does not exist' race survived a
  clean-and-retry; functions and performance hit it back to back)

- all_plugins analyze: full-history checkout + --base-branch, matching
  the format job - flutter_plugin_tools resolves a git base even when
  analyzing everything ('fatal: Not a valid object name main')
…unpin Xcode

Dart's Firebase.apps cannot observe the natively-configured [DEFAULT]
until the first platform-channel call, so the isEmpty guard always
passed and the collision still threw inside initializeApp (confirmed:
installations and crashlytics macOS failed identically with the guard
in place). Catching duplicate-app and keeping the native instance is
the only reliable form; the bundled plists carry the same registered
apps, so the kept instance is correct.

Xcode pins removed from smoke/fdc/pipeline/all_plugins - jobs use the
runner image default (Firebase's minimum is 26.2, below any current
default; the pinned 26.2 SwiftPM also canonicalizes the ephemeral
symlink farm, the suspected root of the functions/performance SPM
resolution failures).
…r no check

A job toggled off by an input still renders as a skipped check, so the
14 products carried ~25 permanently-skipped platform checks on every
PR (wasm x13, windows x9, web x2, macos x1). Each platform is now its
own reusable workflow (changes/android/ios/macos/web/windows, wasm as
an input on web) and each caller declares only the platform jobs its
product supports - an unsupported platform simply does not exist in
the recap, while changes-filter skips (diff cannot affect platform)
stay visible as intended.

Every job body was ported byte-identically from the monolith modulo an
enumerated allow-list (verified by scripted diff against HEAD): Xcode
pin dropped (image default; the pinned 26.2 SwiftPM canonicalizes the
ephemeral symlink farm), the ineffective SPM pre-warm removed, and
functions/performance take the CocoaPods path until default-Xcode runs
prove the SPM resolution clean.

Branch protection: check names become '<platform> / <platform>'
('web-wasm / web' for firestore's wasm leg); entries for unsupported
platforms must be deleted rather than renamed.
…mance iOS project

The guard-v2 edit collapsed the whole try/catch onto one comment line, so
initializeApp was never called. Web has no native [DEFAULT] fallback, so
every web suite registered zero tests (Android/Apple survived on native
auto-init). Verified locally: storage web 82/82 under emulators.

flutter config --no-enable-swift-package-manager does not remove existing
SPM integration from a project; the regenerated performance scaffold had it
baked in, so the ios-spm:false retreat never reached CocoaPods. Removed the
FlutterGeneratedPluginSwiftPackage references per the tool's own migration
notes.
…the Android launch hang

The committed example plists were placeholders, and two mechanisms turned
them into failures now that the native SDKs configure [DEFAULT] from the
bundle before Dart runs:

- auth's plist carried the old invalid-shape 'dummy-api-key', which
  FIRInstallations aborts on at launch - the deterministic 'Unable to
  start the app on the device' on both Apple platforms.
- installations/messaging/crashlytics carried valid-shape dummy values,
  so guard v2 kept a dummy-credential native app and every live-backend
  call (FIS getToken, FCM) failed.

Deleted all of them; the generator already derives real plists from the
injected options (inject runs before generate), auth just needed wiring
into that path.

performance iOS: the regenerated scaffold's pbxproj kept navigator file
references to the plugin's local Swift package and the ephemeral
FlutterFramework; xcodebuild resolves wrapper refs as packages, which is
why de-SPM'ing the integration didn't stop 'ios/firebase_core does not
exist'. Verified locally: resolution now returns empty.

Android: flutter test against the AVD occasionally hangs at app launch
with zero output and burned the whole 20-minute step. New wrapper mirrors
the Apple one (alarm-bounded attempts, one retry, numeric tally).

Apple reusables also dump recent crash reports on failure - the macOS
launch deaths print nothing useful at the Flutter level. Pipeline iOS
build timeout 25 -> 35 (cold SwiftPM fetch overran it).
…y in the step budget

The crash reports from the new forensics step show the macOS launch
deaths are taskgated kills: 'CODESIGNING - Taskgated Invalid Signature'.
auth (applesignin), messaging (aps-environment) and remote_config
(keychain-access-groups, empty) all carry restricted entitlements that
require a provisioning profile CI's ad-hoc signing cannot provide. The
mega tests app never had them and ran these suites green on macOS for
years, so the examples drop them too.

messaging/performance iOS were dying to arithmetic, not to a bug: the
first attempt hangs at launch, the 900s alarm fires as designed, and the
20-minute step timeout then kills the retry mid-build. E2E steps get 40
minutes (two bounded attempts), jobs 60.

installations has no macOS job anymore: every one of its e2e tests skips
itself on macOS pending keychain-sharing work (#9538), so the job could
only report '0 passed, 4 skipped', which the tally guard refuses.
…ests on macOS

The pipeline pods build compiles gRPC-C++/BoringSSL from source, which
alone overran a 35-minute budget; SPM pulls Google's precompiled
grpc/abseil binaries. The resolution failure that forced CocoaPods here
was the pinned Xcode 26.2 symlink canonicalization, and the unpinned
e2e-firestore SPM job now proves the layout resolves.

auth macOS launches since the entitlement fix and runs its suite; the
two checkActionCode tests fail in createUserWithEmailAndPassword with
[firebase_auth/keychain-error], the same keychain-sharing gap (#9538)
that already skips 83 sibling tests on macOS - these two only escaped
because their group's skip covers Windows.
…'s stale package refs; recycle wedged simulators

- The macOS skips added to the two checkActionCode tests replaced the
  enclosing group's Windows skip (package:test metadata merging), which
  un-skipped them on Windows where checkActionCode is unimplemented. The
  per-test condition now covers both platforms.
- messaging's example pbxproj carried the same three stale navigator
  wrapper refs that broke performance: Xcode resolves a wrapper ref to
  the plugin's real ios/ directory, where '../firebase_core' does not
  exist. Verified locally: resolution returns the full package set now.
- A simulator that produces the zero-output launch hang stays wedged;
  the iOS retry burned its second alarm against the same device
  (observed twice in one job). The retry now shuts down, erases and
  reboots the simulator before the second attempt.
- The pipeline build gets the same resolve-race retry the iOS reusable
  has had; its single attempt was the only unguarded SPM build left.
… the simulator around hangs

The pipeline test step burned its whole budget on the zero-output
launch hang (Xcode build done, then silence) with no bound, no retry
and no recovery; it now runs the same alarm + simulator-recycle +
retry + numeric-tally wrapper as reusable_e2e_ios.yaml.

The hang does not reproduce locally: the full messaging suite passes
on a real machine over the identical SPM path (pod deintegrate, real
config). On the retry path both wrappers now dump the simulator's log
for the Runner process before recycling, so the next CI hang shows
what the app was doing instead of nothing.
…s; smoke android gets the retry wrapper

The Flutter tool's SPM migration is what plants the poison: on every
project without committed integration it adds navigator refs including
one to the plugin's real ios/ directory, and xcodebuild resolving that
ref dies on the plugin manifest's '../firebase_core' relative path.
That is why pipeline failed resolution persistently while examples with
committed integration never do. pipeline_example now commits the
migration output minus those navigator refs (verified locally: full
package set resolves), same end state as the stable examples.

The simulator log captured around a hang shows the app alive and
serving analytics traffic while  waits forever - the
failure is the tool's log reader missing the VM service URI, not the
app or the device. So: three bounded attempts (900s cold, 600s warm),
diagnostics after the first failure, simulator recycle only before the
last attempt.

smoke android still ran a bare flutter test inside the AVD action with
a 20-minute budget; it now uses the same retry wrapper and 40 minutes
as reusable_e2e_android.yaml.
… failures

The only red left on the run was performance android dying in
'Pre-build APK' on 'Gradle threw an error while downloading artifacts
from the network' - a transient repository hiccup with no retry
anywhere in the path. Evidence-gated like the other retries: only that
message triggers the second build, real failures still fail fast.
firestore windows died at 'ext.flutter.driver: (112) Service has
disappeared' - the desktop app crashing partway through, an
intermittent native crash the step had no answer for. Retry that exact
shape once; test failures and [E] verdicts keep their first result.
@Lyokone
Lyokone merged commit cfbe35f into main Aug 6, 2026
109 checks passed
@Lyokone
Lyokone deleted the ci/p2-endgame branch August 6, 2026 12:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants