Skip to content

Release dev → main: SKU-aware OTA release artifacts + rollout-bucket compare coverage#57

Merged
adamshiervani merged 4 commits intomainfrom
dev
Apr 27, 2026
Merged

Release dev → main: SKU-aware OTA release artifacts + rollout-bucket compare coverage#57
adamshiervani merged 4 commits intomainfrom
dev

Conversation

@adamshiervani
Copy link
Copy Markdown
Contributor

@adamshiervani adamshiervani commented Apr 27, 2026

Summary

Fast-forwards main to dev (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 the fix/ota-release-artifact-sync track. Introduces the ReleaseArtifact model + migration, SKU-aware default selection, create-only sync-releases.ts, and removes the unused forceUpdate query 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 adds compare-device-2 (rollout bucket 9) alongside compare-device-1 (bucket 81) so the compare script exercises both eligible and ineligible rollout paths.

Verification

  • Unit tests: 52 / 52 pass
  • compare-releases.sh against staging vs prod: 306 / 306 (274 pass, 32 expected dev/prerelease-constraint accepts, 0 fail) — both rollout buckets exercised
  • Sync ran cleanly against the staging DB: existing rows untouched, 2 residual older R2 versions registered at 10% (cosmetic — semver max selection ignores them)

Operational notes for after merge

  1. Run sync-releases against 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.
  2. Error-message wording changed for SKU-incompat 404s. Anything regex-matching the legacy "Version X predates SKU support..." string in alerts/dashboards will need updating.
  3. KNOWN_SKUS is hardcoded in sync-releases.ts — bump it before shipping a new SKU.

Test plan

  • CI passes on PR
  • Manual recheck of compare-releases.sh against https://api.jetkvm.com once main is deployed to prod
  • Run sync-releases against prod DB

Note

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 ReleaseArtifact table/model linked to Release (with migration backfilling existing releases as jetkvm-v2-only) and updates Retrieve to 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 new npm run sync-releases) to create-only register new stable releases/artifacts from R2/S3 (including SKU folder support), adds scripts/compare-releases.sh for local-vs-prod endpoint comparisons with accepted deviation rules, and updates/extends tests (including new sync-releases tests) 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.

* 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.
@adamshiervani adamshiervani marked this pull request as ready for review April 27, 2026 17:49
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ 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.

Comment thread src/releases.ts
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.
@adamshiervani adamshiervani changed the title Sync dev → main: SKU-aware OTA release artifacts + rollout-bucket compare coverage Release dev → main: SKU-aware OTA release artifacts + rollout-bucket compare coverage Apr 27, 2026
@adamshiervani adamshiervani merged commit 412208b into main Apr 27, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant