Guardrail: connected tests refuse to run without an emulator ANDROID_SERIAL

Post-incident: an unscoped connectedDebugAndroidTest fanned out to every
adb device including the personal phone, where the debug test APK's
signature mismatch was resolved by UNINSTALLING the release app — wiping
its data. The v1->v2 Room migration was initially blamed but is innocent
(three emulator replays preserved data; dumpsys firstInstallTime ==
lastUpdateTime proved the phone got a fresh install, not an upgrade).
connected* tasks now fail fast unless ANDROID_SERIAL names an emulator.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Ole-Morten Duesund 2026-06-10 15:39:25 +02:00
commit 5346b6a879
2 changed files with 24 additions and 0 deletions

View file

@ -90,5 +90,9 @@ All gradlew calls need `JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64` — system
- Verify alarms: `adb shell dumpsys alarm | grep meddetsamme``window=0` means exact
- The phone (Pixel 7 Pro) has a RELEASE-signed install: `adb install` of debug builds is
rejected on signature; use release builds for the phone, emulator for debug.
- **INCIDENT RULE (2026-06-10, data was lost): never run a device-touching command
unscoped.** Every `adb` call gets `-s <serial>`; connected tests get
`ANDROID_SERIAL=emulator-<n>`. The build fails connected* tasks without an emulator
serial — that guard stays. The phone is never a test target.
See @README.md for architecture detail.

View file

@ -71,6 +71,26 @@ android {
sourceSets.getByName("androidTest").assets.srcDir("$projectDir/schemas")
}
// Guardrail (added after a real incident): an unscoped connected test run fans
// out to EVERY adb device. On 2026-06-10 that meant the personal phone, where
// the debug test APK's signature mismatch got "resolved" by uninstalling the
// release app — destroying its data. Connected tests now refuse to run unless
// ANDROID_SERIAL pins them to a specific (emulator) device.
tasks.matching { it.name.startsWith("connected") }.configureEach {
doFirst {
val serial = System.getenv("ANDROID_SERIAL")
check(!serial.isNullOrBlank()) {
"Refusing to run connected tests without ANDROID_SERIAL — an unscoped run " +
"targets every adb device, including the phone. " +
"Use: ANDROID_SERIAL=emulator-5554 ./gradlew ${this@configureEach.name}"
}
check(serial.startsWith("emulator-")) {
"ANDROID_SERIAL=$serial is not an emulator — connected tests must never " +
"target a physical device."
}
}
}
// Release builds refresh the bundled FEST dataset when it's >30 days old.
// The script fast-exits when fresh and keeps the old file on download failure,
// so offline/CI release builds never break on this.