fix(firmware): never offer an alpha release older than current stable (#6198)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
James Rich
2026-07-10 12:02:36 -05:00
committed by GitHub
parent 5bdbd33897
commit 9d3e3a1dfd
2 changed files with 25 additions and 1 deletions

View File

@@ -75,7 +75,8 @@ open class FirmwareReleaseRepositoryImpl(
private fun getLatestFirmware(releaseType: FirmwareReleaseType): Flow<FirmwareRelease?> = staleWhileRevalidateFlow(
loadFromCache = {
ensureSeeded()
localDataSource.getLatestRelease(releaseType)?.asExternalModel()
val latest = localDataSource.getLatestRelease(releaseType)?.asExternalModel()
if (releaseType == FirmwareReleaseType.ALPHA) latest.notBelowStable() else latest
},
shouldFetch = { cached ->
cached == null || localDataSource.getLatestRelease(releaseType)?.isStale() != false
@@ -93,6 +94,17 @@ open class FirmwareReleaseRepositoryImpl(
localDataSource.deleteAllFirmwareReleases()
}
/**
* After a stable promotion the alpha channel lags behind stable — never offer a downgrade. Reads the cached stable
* row (every refresh writes both types together, so it is as fresh as the alpha row) rather than combining with
* [stableRelease], which would run a second revalidate pipeline — and potentially a duplicate network refresh — for
* every alpha collector.
*/
private suspend fun FirmwareRelease?.notBelowStable(): FirmwareRelease? {
val stable = localDataSource.getLatestRelease(FirmwareReleaseType.STABLE)?.asExternalModel()
return if (this != null && stable != null && asDeviceVersion() < stable.asDeviceVersion()) stable else this
}
/**
* Applies the bundled snapshot per release type whenever it is newer than what is cached for that type — not just
* when the cache is empty. The bundle is refreshed weekly in CI, so an app update carries fresh data even for users

View File

@@ -228,6 +228,18 @@ class FirmwareReleaseRepositoryImplTest {
)
}
@Test
fun alphaChannelNeverOffersVersionBelowStable() = runBlocking {
// After a promotion the newest alpha (2.7.25) is older than stable (2.7.26) — offer stable instead.
dao.insert(FirmwareReleaseEntity(id = "v2.7.26.54e0d8d", releaseType = FirmwareReleaseType.STABLE))
dao.insert(FirmwareReleaseEntity(id = "v2.7.25.104df5f", releaseType = FirmwareReleaseType.ALPHA))
assertEquals("v2.7.26.54e0d8d", repository.alphaRelease.toList().last()?.id)
// A genuinely newer alpha is still offered.
dao.insert(FirmwareReleaseEntity(id = "v2.7.27.abc1234", releaseType = FirmwareReleaseType.ALPHA))
assertEquals("v2.7.27.abc1234", repository.alphaRelease.toList().last()?.id)
}
@Test
fun olderBundledSnapshotNeverRegressesCache() = runBlocking {
// A successful network refresh left the cache newer than the (weekly) bundle.