Skip to content

fix(update): reset firmware state after completion and fix upload race#108

Merged
JanZachmann merged 1 commit intomainfrom
fix/update-rollback
Mar 1, 2026
Merged

fix(update): reset firmware state after completion and fix upload race#108
JanZachmann merged 1 commit intomainfrom
fix/update-rollback

Conversation

@JanZachmann
Copy link
Contributor

@JanZachmann JanZachmann commented Mar 1, 2026

Summary

  • Clear stale firmware state after update: Core now clears update_manifest and firmware_upload_state when a firmware update completes (Succeeded or Recovered), so the update page is fresh after reconnection. A new is_actual_update_result() helper distinguishes these from NoUpdate (which is used for polling only).
  • Fix upload TypeError race in UpdateFileUpload.vue: sync.ts replaces the deviceOperationState object reference on every Core render. The previous { deep: true } watcher fired on each render and could clear updateFile.value while an axios POST was in-flight, causing Cannot read properties of undefined (reading 'name'). Fixed by guarding with an oldState transition check and capturing the file reference before await.
  • Fix update validation modal across SPA re-logins: The modal watcher now uses a combined watch on status, ackedInHealthcheck (live Core value), and ackedOnMount (onMounted snapshot) so a second rollback/success modal appears correctly in the same SPA session after a second update.
  • Add VITE_FIRMWARE_UPDATE_TIMEOUT_MS test override and wire into startReconnectionPolling alongside the existing reboot/factory-reset overrides.
  • Rename SOCKET_PATHDEVICE_SERVICE_SOCKET_PATH with updated default path.

Reason

  • Post-rollback, the update page retained the old manifest version and upload state, confusing users about whether the update was in progress.
  • Real-device testing revealed the upload TypeError: after reconnection, many in-flight HealthcheckResponse(Ok(Recovered)) events each triggered a Core render, flooding the watcher and racing with a user-initiated upload.
  • The second rollback modal (same SPA session) was suppressed because updateValidationAckedOnMount was stale after re-login without a page reload.

- Add is_actual_update_result() helper to distinguish Succeeded/Recovered
  from NoUpdate; the existing is_update_complete() matches all three for
  polling purposes but cleanup must only run on actual update results
- Clear model.update_manifest and firmware_upload_state in Core when a
  firmware update completes (Succeeded or Recovered) so the update page
  is fresh after reconnection
- Fix upload TypeError race in UpdateFileUpload.vue: sync.ts replaces the
  deviceOperationState object reference on every Core render, so the { deep: true }
  watcher fired on each render and cleared updateFile.value while an axios
  POST was in-flight, causing "Cannot read properties of undefined (reading 'name')";
  guard with oldState transition check and capture file reference before await
- Expose updateValidationAcked from Core healthcheck through sync.ts/types
  so App.vue can use the live value across SPA re-logins instead of the
  stale onMounted snapshot
- Use combined watcher on status + ackedInHealthcheck + ackedOnMount to
  correctly show the update validation modal on re-login in the same SPA
  session after a second update
- Snapshot factoryResetError/factoryResetContext and updateValidationIsRollback
  into local refs so the dialog template is decoupled from the live ViewModel
  during close animation
- Add FIRMWARE_UPDATE_TIMEOUT_OVERRIDE_MS test env var and wire into
  startReconnectionPolling alongside the existing reboot/factory-reset overrides
- Rename SOCKET_PATH env var to DEVICE_SERVICE_SOCKET_PATH with updated default
- Add E2E tests for rollback notification, timeout state, second rollback
  modal in same SPA session, update success, and upload regression after rollback

Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
@JanZachmann JanZachmann merged commit f7f7ee3 into main Mar 1, 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