Release dev → main: SKU-aware OTA release artifacts + rollout-bucket compare coverage#57
Merged
adamshiervani merged 4 commits intomainfrom Apr 27, 2026
Merged
Release dev → main: SKU-aware OTA release artifacts + rollout-bucket compare coverage#57adamshiervani merged 4 commits intomainfrom
adamshiervani merged 4 commits intomainfrom
Conversation
* feat: add SKU-aware OTA release artifacts Persist OTA artifact URL/hash data separately from rollout state so stable release responses can choose artifacts by compatible SKU while release rollout remains version/type based. * fix: select compatible OTA releases by SKU Ensure stable release selection only considers releases with artifacts compatible with the requested SKU, and tighten tests around the DB-backed OTA contract. * fix: match production OTA release responses Only expose stable signature URLs that actually exist and preserve production's version-first SKU error behavior. * fix: restrict legacy OTA artifacts and make sync create-only Pre-SKU artifacts (no skus/ folder) are jetkvm-v2 only. Marking them compatible with jetkvm-v2-sdmmc would brick devices that received firmware predating their hardware. Future SKUs must opt in via an explicit skus/<sku>/ upload. sync-releases now skips releases already in the DB instead of upserting them. This prevents routine sync runs from rewriting Release.url/hash or appending duplicate ReleaseArtifact rows if R2_CDN_URL ever changes. Backfills and repairs are left to one-off scripts. * refactor: drop forceUpdate query parameter from /releases The flag is no longer sent by any client. Routine update checks now always go through the rollout-aware default-and-latest path, which is what forceUpdate effectively short-circuited to. Removes one query parameter, one branch in the handler, and the corresponding axis from the compare-releases sweep. * fix: skip incompatible defaults and parallelize stable DB lookups getDefaultRelease previously picked the newest 100%-rolled-out release without checking SKU compatibility. If that release lacked a compatible artifact, the request 404'd downstream even though older 100%-rolled-out releases had valid binaries for the SKU. It now filters to releases that actually ship a compatible artifact before selecting the latest, falling back to a 404 only when no compatible default exists. The four DB lookups in the stable rollout-aware path are independent; run them concurrently so background-check latency drops from ~4 round trips to ~1.
The release API used to emit "Version X predates SKU support..." for every SKU-incompat 404. Upcoming changes route this through getDefaultRelease and emit "No default <type> release available for SKU..." instead. Both 404s mean the same thing to the device, so accept either wording in the diff tolerator.
Adds a regression test for the rollout-aware path: when the in-progress release has no compatible artifact for the requested SKU and the device is outside the rollout window, the response must keep the default release instead of throwing. Also extends compare-releases.sh with a second device ID picked to land in a low rollout bucket (bucket 9 vs the existing bucket 81), so the script now exercises both eligible and ineligible rollout paths.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 68b1000. Configure here.
After the getDefaultRelease SKU-compat fix, the default path was graceful but the rollout-upgrade path stayed strict: if a device was in the rollout bucket and the latest release lacked a compatible artifact for the requested SKU, dbReleaseToMetadata still threw and 404'd the whole request — even though responseJson already held a valid default. Short-circuit the upgrade when the latest release has no compatible artifact and update the regression test to assert the default is kept instead of asserting the throw.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
Fast-forwards
maintodev(3 commits ahead). Combines the SKU-aware OTA work with two compare-script improvements made on top of it.Changes
b24a057— Add SKU-aware OTA release artifacts (Add SKU-aware OTA release artifacts #56) — squash-merge of thefix/ota-release-artifact-synctrack. Introduces theReleaseArtifactmodel + migration, SKU-aware default selection, create-onlysync-releases.ts, and removes the unusedforceUpdatequery param.b9a1298— test: accept both SKU-incompat 404 messages in compare-releases — widens the diff tolerator so"No default <type> release available for SKU \"X\""is treated as equivalent to the legacy"Version X predates SKU support and cannot serve SKU \"Y\"". Both 404s mean the same thing to a device.68b1000— test: cover rollout-bucket fallback for SKU-incompat upgrades — adds a regression test pinning the new graceful behavior (default kept when an in-rollout release has no compat artifact and the device is outside the bucket). Also addscompare-device-2(rollout bucket 9) alongsidecompare-device-1(bucket 81) so the compare script exercises both eligible and ineligible rollout paths.Verification
compare-releases.shagainst staging vs prod: 306 / 306 (274 pass, 32 expected dev/prerelease-constraint accepts, 0 fail) — both rollout buckets exercisedOperational notes for after merge
sync-releasesagainst the prod DB once after deploy. The migration runs automatically; the sync does not. Same two residual rows (app 0.4.0,system 0.1.12) will appear at 10% in prod, harmless per First-time setup instructions #1 above."Version X predates SKU support..."string in alerts/dashboards will need updating.KNOWN_SKUSis hardcoded insync-releases.ts— bump it before shipping a new SKU.Test plan
compare-releases.shagainsthttps://api.jetkvm.comonce main is deployed to prodsync-releasesagainst prod DBNote
High Risk
High risk because it changes OTA release selection logic and introduces a new relational model/migration that affects how devices receive firmware (including rollout behavior and SKU compatibility). Also adds new sync tooling that writes production release rows, so mistakes could impact update availability.
Overview
Stable OTA releases are now SKU-aware and DB-backed. Adds a new
ReleaseArtifacttable/model linked toRelease(with migration backfilling existing releases asjetkvm-v2-only) and updatesRetrieveto select the first artifact compatible with the requested SKU.Release discovery flow changes. Stable requests no longer upsert from S3; instead they read from DB (prerelease requests still resolve via S3). Version-constrained stable requests bypass rollout while still using DB metadata, and background rollout now falls back to the latest 100%-rolled-out compatible release when the newest release lacks an artifact for that SKU.
Operational tooling & verification updates. Adds
scripts/sync-releases.ts(invoked via newnpm run sync-releases) to create-only register new stable releases/artifacts from R2/S3 (including SKU folder support), addsscripts/compare-releases.shfor local-vs-prod endpoint comparisons with accepted deviation rules, and updates/extends tests (including newsync-releasestests) plus disables Vitest file parallelism.Reviewed by Cursor Bugbot for commit eebf332. Bugbot is set up for automated code reviews on this repo. Configure here.