ci(spike): PoC shard API 37 preview E2E + feasibility doc

Feasibility spike for sharding the e2e-preview job (the hand-provisioned
API 37 / google_apis_ps16k 16 KB-page emulator), CI's longest leg
(~16.4-17.6 min). docs/perf/api37-e2e-sharding-spike.md breaks the leg into
fixed overhead B ~8.3 min (setup + boot + Gradle daemon/config/compile/install)
vs parallelizable test execution T ~8.8 min, models B + T/N for N=2/3/4, and
recommends N=2 (~17.1 -> ~12.7 min, ~28% off the critical path) capped by the
API 30 matrix wall (~12.0 min) beyond N=3.

DRAFT PoC (do NOT merge as-is): converts e2e-preview to a strategy.matrix.shard
[0, 1] fan-out passing AndroidJUnitRunner numShards/shardIndex through the
existing -Pandroid.testInstrumentationRunnerArguments.* channel (no GMD, no
orchestrator, no Gradle change). Artifact names gain a shard suffix;
ci-passed still lists e2e-preview once (matrix fan-in keeps the single gate).
Local preflight stays single-emulator. Relates to #258.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-06 14:31:40 -05:00
co-authored by Claude Opus 4.8
parent b8d67557a0
commit 66da643249
2 changed files with 263 additions and 3 deletions
+23 -3
View File
@@ -314,8 +314,20 @@ jobs:
name: E2E (API 37 preview)
runs-on: ubuntu-latest
timeout-minutes: 35
# SPIKE / PoC (docs/perf/api37-e2e-sharding-spike.md) — DRAFT, DO NOT MERGE AS-IS.
# Shards the instrumented suite across N parallel API 37 emulators (each a separate matrix leg
# that hand-provisions its OWN emulator and runs one shard via AndroidJUnitRunner numShards/
# shardIndex). Projected: ~17.1 min -> ~12.7 min (N=2), cutting the pipeline's critical path
# ~28%. `ci-passed` still lists `e2e-preview` once; GHA rolls all shard legs under it (a matrix
# job's result is `failure` if any shard fails), so no branch-protection change is needed.
# Keep API37_NUM_SHARDS in lockstep with the length of matrix.shard.
strategy:
fail-fast: false
matrix:
shard: [0, 1] # 0-based shardIndex values; length must equal API37_NUM_SHARDS below
env:
API37_IMAGE: "system-images;android-37.0;google_apis_ps16k;x86_64"
API37_NUM_SHARDS: "2"
steps:
- name: Check out source
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
@@ -432,7 +444,13 @@ jobs:
[ "$booted" = "1" ] || { echo "::error::API 37 preview emulator failed to boot after 2 attempts"; exit 1; }
adb shell input keyevent 82 || true
./gradlew connectedDebugAndroidTest --stacktrace
# SPIKE: run only this matrix leg's shard. AndroidJUnitRunner hashes each test name into
# one of numShards buckets and runs only shardIndex's bucket. The args flow through AGP's
# -Pandroid.testInstrumentationRunnerArguments.* channel (same one local_instrumented.py
# uses for .class=…) — no GMD, no orchestrator, no Gradle-side change.
./gradlew connectedDebugAndroidTest --stacktrace \
"-Pandroid.testInstrumentationRunnerArguments.numShards=${API37_NUM_SHARDS}" \
"-Pandroid.testInstrumentationRunnerArguments.shardIndex=${{ matrix.shard }}"
- name: Dump emulator log on failure
if: failure()
@@ -446,7 +464,8 @@ jobs:
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: e2e-api37-boot-diagnostics
# SPIKE: shard-unique name — upload-artifact@v7 errors on duplicate artifact names.
name: e2e-api37-boot-diagnostics-shard${{ matrix.shard }}
path: |
${{ runner.temp }}/emulator.log
${{ runner.temp }}/logcat.txt
@@ -461,7 +480,8 @@ jobs:
if: ${{ !cancelled() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: e2e-test-report-api37-preview
# SPIKE: shard-unique name — upload-artifact@v7 errors on duplicate artifact names.
name: e2e-test-report-api37-preview-shard${{ matrix.shard }}
path: app/build/reports/androidTests/connected/
if-no-files-found: warn