fix(android): preserve rollout update chain

This commit is contained in:
fy59 2026-09-17 15:56:47 +02:00
parent 8aa3c20954
commit 0018edff26
5 changed files with 26 additions and 4 deletions

View file

@ -6,6 +6,10 @@
candidate so user services cannot mix a versioned install with live-checkout candidate so user services cannot mix a versioned install with live-checkout
helpers during rollout. helpers during rollout.
- Added a bounded Android package-version override and made the schema-v17
backup bridge produce a signed release APK, allowing a strictly increasing
private update chain without changing Trainlog schemas or protocols.
- Closed the native candidate warning gate with checked path and HTTP response - Closed the native candidate warning gate with checked path and HTTP response
bounds, strict GCC and Clang builds, full Clang ASan/UBSan validation, and a bounds, strict GCC and Clang builds, full Clang ASan/UBSan validation, and a
stable concurrent object-I/O MTP double. Rebuilt the desktop, current Android stable concurrent object-I/O MTP double. Rebuilt the desktop, current Android

View file

@ -35,6 +35,16 @@ val releaseSigningComplete = listOf(
releaseKeyPassword, releaseKeyPassword,
).all { !it.isNullOrBlank() } ).all { !it.isNullOrBlank() }
/* CONTRACT: a private rollout may need an update-only version code above the
* repository default while preserving versionName, schemas and protocols.
* INVARIANT: the override remains a positive Android package version code and
* never changes the source default used by ordinary builds. */
val trainlogVersionCode =
providers.environmentVariable("TRAINLOG_ANDROID_VERSION_CODE").orNull?.let { raw ->
raw.toIntOrNull()?.takeIf { it in 1..Int.MAX_VALUE }
?: throw GradleException("TRAINLOG_ANDROID_VERSION_CODE must be a positive integer")
} ?: 3
android { android {
namespace = "com.labfytools.trainlog" namespace = "com.labfytools.trainlog"
compileSdk = 37 compileSdk = 37
@ -44,7 +54,7 @@ android {
minSdk = 26 minSdk = 26
targetSdk = 36 targetSdk = 36
versionCode = 3 versionCode = trainlogVersionCode
versionName = "0.1.2" versionName = "0.1.2"
testInstrumentationRunner = testInstrumentationRunner =
"androidx.test.runner.AndroidJUnitRunner" "androidx.test.runner.AndroidJUnitRunner"

View file

@ -28,7 +28,10 @@ the separately authorized rollout are complete.
must not combine a packaged binary with helpers from a live checkout. must not combine a packaged binary with helpers from a live checkout.
2. `assembleDebug` is development evidence only. Rollout requires the existing 2. `assembleDebug` is development evidence only. Rollout requires the existing
release signing identity, verification of its public certificate and a valid release signing identity, verification of its public certificate and a valid
increasing `versionCode`; this mission reads no signing secret. increasing `versionCode`. A bounded `TRAINLOG_ANDROID_VERSION_CODE` build
override may establish an update-only private rollout chain without changing
the product version, schema or protocol. The bridge recipe also produces a
signed release variant for installation; debug APKs remain test evidence.
3. Quiesce old writers only during an authorized rollout. Desktop backup uses 3. Quiesce old writers only during an authorized rollout. Desktop backup uses
`backup_trainlog_sqlite.py` and SQLite's backup API, then integrity and `backup_trainlog_sqlite.py` and SQLite's backup API, then integrity and
foreign-key checks. foreign-key checks.

View file

@ -523,6 +523,11 @@ canonical stable Android artifact. Release assets must come from the exact
tagged source state, and the tag, Android `versionName`, Meson project version, tagged source state, and the tag, Android `versionName`, Meson project version,
TUI product version, asset names, and release title must agree. TUI product version, asset names, and release title must agree.
Private update rollout may set `TRAINLOG_ANDROID_VERSION_CODE` to a positive
integer while keeping `versionName=0.1.2`. This build-only override is used only
to maintain a strictly increasing installed → backup bridge → current APK
chain; it does not change any schema or exchange-format version.
```bash ```bash
meson setup --reconfigure build meson setup --reconfigure build
meson compile -C build meson compile -C build

View file

@ -79,7 +79,7 @@ def main():
":app:testDebugUnitTest", ":app:testDebugUnitTest",
"--tests", "--tests",
"com.labfytools.trainlog.data.BackupBridgeServiceTest", "com.labfytools.trainlog.data.BackupBridgeServiceTest",
":app:assembleDebug", ":app:assembleRelease",
], ],
cwd=android, cwd=android,
env=env, env=env,
@ -105,7 +105,7 @@ def main():
print( print(
f"BRIDGE_RECIPE={subprocess.check_output(['git','rev-parse','HEAD'],cwd=ROOT,text=True).strip()}" f"BRIDGE_RECIPE={subprocess.check_output(['git','rev-parse','HEAD'],cwd=ROOT,text=True).strip()}"
) )
print(f"BRIDGE_APK={android/'app/build/outputs/apk/debug/app-debug.apk'}") print(f"BRIDGE_APK={android/'app/build/outputs/apk/release/app-release.apk'}")
if __name__ == "__main__": if __name__ == "__main__":