Retry only failed Android E2E flows - #57998
Closed
cipolleschi wants to merge 10 commits into
Closed
Conversation
|
@cipolleschi has imported this pull request. If you are a Meta employee, you can view this in D116463690. |
cipolleschi
force-pushed
the
cipolleschi/ci/android-e2e-selective-retries
branch
from
August 18, 2026 19:38
29f35f4 to
eab1a5b
Compare
|
@cipolleschi merged this pull request in 4c022bb. |
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.
Summary:
Android Maestro E2E jobs currently stop after the first flow that exhausts its in-process retries. The workflow-level retry then starts the whole suite again, including flows that already passed.
This changes the retry model so that:
passed,failed, andpendingstate is saved atomically after every flow;The Android E2E timeout is increased from 60 to 90 minutes to give the initial all-flows attempt enough time to finish. The existing three workflow attempts now provide up to three executions per failing flow instead of multiplying workflow retries by in-process retries.
CI follow-ups
RNTester Debug completed all 40 flows with 38 passes and 2 failures. Retry 1 downloaded its state, skipped exactly those 38 passing flows, and executed only the two failures, validating the selective retry behavior.
RNTester Release became unhealthy after an early flow failure and timed out while later flows were still pending. The state file was being written incrementally, but its artifact upload was inside the timed composite action and was killed by the same timeout. Moved state upload into the outer reusable workflow so
if: always()preserves passed, failed, and pending results after an E2E timeout.The next Release retry downloaded the preserved state, skipped its 7 prior passes, reran the remaining flows, and recovered to 40/40. A later matrix retry could still start that completed flavor and fail during an unnecessary APK installation, so retry jobs now skip the entire E2E action when downloaded state is fully passed.
Both
test_jsvariants failed because the repository Jest preset throws wheneverconsole.erroris called, and the new failure-path unit test intentionally exercised a production error log. The test now mocks that expected log while continuing to assert the aggregate flow failure.RNTester Debug consistently failed
image-wide-gamut.ymlwith exactly 83.974% screenshot similarity andlegacy-native-module.ymlbefore the APIs search became visible, while Release passed both immediately. Reproducing Debug with an offline Android emulator showed the actual cause: importingImageExample.jseagerly starts a Facebook image prefetch, and its promise could reject before the Image Loading Events example attached its rejection callback. That produced anUncaught (in promise): UnknownHostExceptionLogBox notification, which covered the screenshot and intercepted the APIs tab. The prefetch now attaches a rejection handler immediately while preserving the existing example-level success/failure reporting. No LogBox dismissal or E2E-specific workaround remains.The remaining
image-wide-gamut.ymlfailure was separate from LogBox: the flow explicitly acceptedP3: errorbut then compared against a golden screenshot containing the successfully loaded Display-P3 fixture. The same ICC-profiled WebKit sample is now embedded as a compact data URI, removing the external network dependency, and the flow requiresP3: loadedbefore screenshot comparison.Changelog:
[INTERNAL] [CHANGED] - Retry only failed or unfinished Android Maestro E2E flows.
Test Plan:
git diff --check(passed)node --check .github/workflow-scripts/maestro-android.js(passed)yarn jest .github/workflow-scripts/__tests__/maestro-android-test.js --runInBand --config '{"testEnvironment":"node","transform":{}}'(passed)ruby -ryaml -e "ARGV.each { |file| YAML.parse_file(file) }" .github/actions/maestro-android/action.yml .github/workflows/e2e-android-rntester.yml .github/workflows/e2e-android-templateapp.yml .github/workflows/test-all.yml(passed)yarn prettier --check .github/workflow-scripts/maestro-android.js .github/workflow-scripts/__tests__/maestro-android-test.js .github/actions/maestro-android/action.yml .github/workflows/e2e-android-rntester.yml .github/workflows/e2e-android-templateapp.yml .github/workflows/test-all.yml(passed)Verified the retry-state
jqpredicate returns false only when every recorded flow has passed../node_modules/.bin/prettier --check packages/rn-tester/js/examples/Image/ImageExample.js(passed)Reproduced on a local Android Debug emulator with external DNS unavailable. Before the fix,
logcatreportedUncaught (in promise): UnknownHostExceptionand LogBox covered the bottom navigation. After the fix, no unhandled rejection or LogBox appeared, the APIs tab displayedexplorer_search, and the wide-gamut example was unobstructed.Verified on a local Android Debug emulator with external DNS unavailable that the embedded Display-P3 fixture reaches
P3: loadedand renders without a network request.The next Debug run passed the stricter
P3: loadedgate but still reproduced the exact 83.974% screenshot mismatch, followed by the legacy APIs-search failure. This confirms the embedded Display-P3 fixture is loading and that a second Debug-only UI state remains. The previous action skipped its Maestro-log upload because the emulator wrapper masked the inner script failure, and it pulledscreen.mp4before stopping the recorder, leaving an unplayable artifact. The runner now captures a PNG, UI hierarchy, logcat, and Metro output when each flow fails, stopsscreenrecordbefore pulling the MP4, and always uploads the diagnostic bundle. The next run will expose the actual obstructing UI so it can be fixed at its source.The first diagnostic run exposed a flaw in the new evidence collection itself: Debug remained inside the E2E action far beyond its normal duration because the
uiautomator/ADB capture commands were unbounded. Every diagnostic subprocess now has a 15-second timeout, so a problem collecting evidence is logged but can never stall later flows or prevent state persistence.The captured screenshots and hierarchy identified the root cause shared by all remaining Debug failures (including one additional FlatList flow):
StaticViewConfigValidatorreported thatAndroidTextInput.validAttributes.fontVariationSettingshad the wrong value. The object-syntax change in Accept object font variation settings (#57929) #57929 added a JS processor to the static TextInput config, but runtime native view-config reflection still derived plaintruefrom the nativeStringprop. Debug validation therefore emitted a LogBox; its notifications covered the wide-gamut screenshot and FlatList controls, and its expanded console blocked the legacy APIs tab. Native view-config construction now recognizesfontVariationSettingsand installs the same processor as the static config. This both makes validation agree and preserves object-to-string normalization when reflected native configs are active. A regression test verifies the reflected attribute matches the static processor and serializes object settings deterministically.Targeted regression test:
yarn test packages/react-native/Libraries/ReactNative/__tests__/getNativeComponentAttributes-test.js --runInBand(passed).Both
test_jsvariants then failed at ESLint before running tests because the new regression test lacked the required Flow file annotation and its CommonJS imports were not in repository order. Added@flow strict-localand reordered the requires; the targeted Jest test and Prettier check remain green.Final Android validation on
bd55f0aa4226: the initial RNTester emulators lost ADB after three passing flows, so the outer state artifacts preserved those passes and marked the remaining work for retry. Retry 1 started fresh emulators, skipped the three prior passes, ran the remaining 37 flows, and finished with 40/40 passed in both Debug and Release; retry 2 was skipped.image-wide-gamut.yml,legacy-native-module.yml, andflatlist-inverted-recycle-maintainvisible.ymleach passed on their first execution after the view-config fix. All Android checks are green, including Android builds, HelloWorld, TemplateApp E2E, and RNTester E2E.