Skip to content

[ci] Retry on-device install, fail fast, diagnose failures, always capture logcat - #11831

Merged
jonathanpeppers merged 6 commits into
mainfrom
dev/simonrozsival/install-failfast
Jul 1, 2026
Merged

[ci] Retry on-device install, fail fast, diagnose failures, always capture logcat#11831
jonathanpeppers merged 6 commits into
mainfrom
dev/simonrozsival/install-failfast

Conversation

@simonrozsival

@simonrozsival simonrozsival commented Jul 1, 2026

Copy link
Copy Markdown
Member

Description

The on-device package-test template apk-instrumentation.yaml builds+installs the app with -t:Install and then runs dotnet test. When the emulator drops off ADB mid-install (error XAGCPU7000: Mono.AndroidTools.AdbException: device offline) the previous behavior was doubly bad:

  1. The install ran with continueOnError: true, so the failure was swallowed and the pipeline still ran dotnet test against a device where the app was never installed, producing a misleading INSTRUMENTATION_FAILED and only going red at the final fail if any issues occurred gate — after wasting time.
  2. A single transient ADB blip failed the whole lane with no attempt to recover, and we captured nothing to explain why the install failed.

Changes

  • Retry the install via the run-dotnet-preview template's existing retryCountOnTaskFailure: 3, so a transient device offline blip can recover on a retry instead of failing the lane.
  • Fail fast (continueOnError: false) once retries are exhausted: the lane fails immediately and the run step (whose condition defaults to succeeded()) is skipped — no point running tests against a device with no app installed.
  • Diagnose install failures: a new failed()-gated, time-bounded step right after install snapshots device state so we can classify the failure next time instead of guessing — connectivity (adb devices -l / get-state), disk pressure (df, dumpsys diskstats), storage-service readiness (dumpsys storaged — the StorageStatsManager NPE seen during install-create), boot completion, and accumulated test apps (pm list packages -3).
  • Never lose logcat: the capture logcat step's condition is changed from the default succeeded() to always(), so the best-effort adb logcat -d runs on success, on a failed step (e.g. fail-fast install), and on job cancellation/timeout (e.g. a hung test). The capture is best-effort (continueOnError: true + || echo) and tolerates an offline device. This matches the step's "Always capture full device logcat" intent and addresses review feedback.

Why always() (logcat) and not just removing the condition

Omitting condition: is not neutral — an Azure Pipelines step with no condition defaults to succeeded(), i.e. it is skipped as soon as any prior step fails. That is exactly the old, buggy behavior. succeededOrFailed() fixes the failed-step case but still skips on cancellation (a job-level timeout counts as cancellation), which is precisely when a hung test's logcat is most valuable — so always() is used.

Behavior change

  • A genuinely unrecoverable install failure now fails the job at the install step (after retries) instead of the final gate.
  • Because the flavors (Debug / aab / NoAab / CoreCLR / …) share one job and downstream steps are gated on succeeded(), a hard install failure in one flavor will now skip the remaining flavors in that job. This is acceptable: a device offline across all retries is very unlikely to install the next flavor either, and fail-fast gives a clear signal.

Context

Tracking issue for the underlying emulator/ADB install flakiness (device offline and the StorageStatsManager NPE): #11830

Observed on build 1488505 (Package Tests > macOS > Tests > APKs 1).

A companion PR adds an equivalent device-state snapshot to the DeviceTest on-failure teardown for the MSBuildDeviceIntegration DeployToDevice/InstallAndRun tests.

The -t:Install step in apk-instrumentation.yaml ran with
continueOnError: true, so an install failure (e.g. XAGCPU7000
"device offline" when the emulator drops off ADB) was swallowed and
the pipeline still ran 'dotnet test' against a device where the app was
never installed. That wastes CI time and produces a misleading
INSTRUMENTATION_FAILED, with the lane only going red at the final
'fail if any issues occurred' gate.

Set continueOnError: false on the install step so a failed install
fails the lane immediately and the test run is skipped (its
condition defaults to succeeded()).

Context: #11830

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 1, 2026 08:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the on-device CI template to fail the job immediately when the -t:Install step fails (e.g., when the emulator/device goes offline), avoiding a subsequent dotnet test run against a device where the test app was never installed.

Changes:

  • Switch the install/build step from continueOnError: true to continueOnError: false to stop the lane as soon as install fails.
  • Add inline rationale in the YAML explaining why fail-fast is preferred (reduces wasted CI time and misleading instrumentation failures).
Show a summary per file
File Description
build-tools/automation/yaml-templates/apk-instrumentation.yaml Makes the install step fail-fast so the job doesn’t proceed to test execution after an install failure.

Copilot's findings

  • Files reviewed: 1/1 changed files
  • Comments generated: 1

Comment thread build-tools/automation/yaml-templates/apk-instrumentation.yaml Outdated
Wrap the -t:Install step in a retry loop (3 attempts, 15s delay between
them) so a transient emulator/ADB drop (XAGCPU7000 "device offline")
during install can recover instead of failing the lane outright. Only if
all 3 attempts fail does the step exit non-zero; combined with
continueOnError: false, the lane then fails fast and the test run is
skipped.

Context: #11830

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival simonrozsival changed the title [ci] Fail fast when on-device app install fails [ci] Retry on-device app install 3x with delay, then fail fast Jul 1, 2026
simonrozsival and others added 3 commits July 1, 2026 10:19
Address review feedback on PR #11831: with continueOnError: false on the
-t:Install step, a failed install skips all subsequent succeeded()-gated
steps, including the capture-logcat step - losing device logs for exactly
the 'device offline' scenario this change targets.

Change the capture-logcat step's condition to succeededOrFailed() so the
best-effort 'adb logcat -d' still runs after a failed install (it is
already continueOnError: true and tolerates an offline device). This
matches the step's stated 'Always capture full device logcat' intent.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the inline PowerShell retry loop with the run-dotnet-preview
template's existing retryCountOnTaskFailure parameter (set to 3). This
keeps the shared template invocation instead of duplicating the dotnet
PATH/invocation logic, while still retrying the -t:Install step on
transient emulator/ADB 'device offline' failures before failing fast.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use always() instead of succeededOrFailed() for the capture-logcat step
so device logs are also captured when the job is canceled - e.g. a
hung/timed-out test run - not just on a failed step. This matches the
step's 'Always capture full device logcat' intent; the capture is
best-effort (continueOnError + '|| echo').

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival simonrozsival changed the title [ci] Retry on-device app install 3x with delay, then fail fast [ci] Retry on-device install, fail fast, and always capture logcat Jul 1, 2026
Add a best-effort, time-bounded device-state snapshot right after the
-t:Install step, gated on failed(), so when an install fails we capture
the data needed to classify it next time instead of guessing:
connectivity (adb devices/get-state), disk pressure (df, dumpsys
diskstats), storage-service readiness (dumpsys storaged - the
StorageStatsManager NPE seen during install-create), boot completion,
and how many test apps have accumulated.

Context: #11830

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival simonrozsival changed the title [ci] Retry on-device install, fail fast, and always capture logcat [ci] Retry on-device install, fail fast, diagnose failures, always capture logcat Jul 1, 2026
@simonrozsival simonrozsival added the copilot `copilot-cli` or other AIs were used to author this label Jul 1, 2026
@jonathanpeppers
jonathanpeppers merged commit 36df374 into main Jul 1, 2026
36 of 38 checks passed
@jonathanpeppers
jonathanpeppers deleted the dev/simonrozsival/install-failfast branch July 1, 2026 13:13
@github-actions github-actions Bot locked and limited conversation to collaborators Aug 1, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

copilot `copilot-cli` or other AIs were used to author this flaky-tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants