From d9f8054906c9674deca0b707a30d327008397fac Mon Sep 17 00:00:00 2001
From: Siyuan
Date: Mon, 17 Nov 2025 14:53:47 +0000
Subject: [PATCH] feat: disable auto-update when custom version update is
detected
---
internal/ota/ota.go | 8 ++++++++
internal/ota/rpc.go | 1 +
internal/ota/state.go | 9 +++++++++
ota.go | 7 +++++++
ui/localization/messages/da.json | 1 +
ui/localization/messages/de.json | 1 +
ui/localization/messages/en.json | 1 +
ui/localization/messages/es.json | 1 +
ui/localization/messages/fr.json | 1 +
ui/localization/messages/it.json | 1 +
ui/localization/messages/nb.json | 1 +
ui/localization/messages/sv.json | 1 +
ui/localization/messages/zh.json | 1 +
ui/src/routes/devices.$id.settings.general.update.tsx | 5 +++++
ui/src/utils/jsonrpc.ts | 1 +
15 files changed, 40 insertions(+)
diff --git a/internal/ota/ota.go b/internal/ota/ota.go
index 589aba2e..534c6976 100644
--- a/internal/ota/ota.go
+++ b/internal/ota/ota.go
@@ -186,6 +186,14 @@ func (s *State) doUpdate(ctx context.Context, params UpdateParams) error {
}
if s.rebootNeeded {
+ if appUpdate.customVersionUpdate || systemUpdate.customVersionUpdate {
+ scopedLogger.Info().Msg("disabling auto-update due to custom version update")
+ if _, err := s.setAutoUpdate(false); err != nil {
+ scopedLogger.Warn().Err(err).Msg("Failed to disable auto-update")
+ }
+ return nil
+ }
+
scopedLogger.Info().Msg("System Rebooting due to OTA update")
redirectUrl := fmt.Sprintf("/settings/general/update?version=%s", systemUpdate.version)
diff --git a/internal/ota/rpc.go b/internal/ota/rpc.go
index 84976025..30f132ea 100644
--- a/internal/ota/rpc.go
+++ b/internal/ota/rpc.go
@@ -156,6 +156,7 @@ func remoteMetadataToComponentStatus(
componentStatus.available = componentStatus.version != componentStatus.localVersion
if componentStatus.available {
componentStatus.availableReason = fmt.Sprintf("custom version %s is not equal to local version %s", constraint, componentStatus.localVersion)
+ componentStatus.customVersionUpdate = true
}
} else if !componentExists {
componentStatus.available = false
diff --git a/internal/ota/state.go b/internal/ota/state.go
index 1bb12033..2bb7055e 100644
--- a/internal/ota/state.go
+++ b/internal/ota/state.go
@@ -38,6 +38,7 @@ type UpdateStatus struct {
Remote *UpdateMetadata `json:"remote"`
SystemUpdateAvailable bool `json:"systemUpdateAvailable"`
AppUpdateAvailable bool `json:"appUpdateAvailable"`
+ WillDisableAutoUpdate bool `json:"willDisableAutoUpdate"`
// only available for debugging and won't be exported
SystemUpdateAvailableReason string `json:"-"`
@@ -59,6 +60,7 @@ type componentUpdateStatus struct {
pending bool
available bool
availableReason string // why the component is available or not available
+ customVersionUpdate bool
version string
localVersion string
url string
@@ -98,6 +100,9 @@ type HwRebootFunc func(force bool, postRebootAction *PostRebootAction, delay tim
// ResetConfigFunc is a function that resets the config
type ResetConfigFunc func() error
+// SetAutoUpdateFunc is a function that sets the auto-update state
+type SetAutoUpdateFunc func(enabled bool) (bool, error)
+
// GetHTTPClientFunc is a function that returns the HTTP client
type GetHTTPClientFunc func() HttpClient
@@ -125,6 +130,7 @@ type State struct {
getLocalVersion GetLocalVersionFunc
onStateUpdate OnStateUpdateFunc
resetConfig ResetConfigFunc
+ setAutoUpdate SetAutoUpdateFunc
}
func toUpdateStatus(appUpdate *componentUpdateStatus, systemUpdate *componentUpdateStatus, error string) *UpdateStatus {
@@ -145,6 +151,7 @@ func toUpdateStatus(appUpdate *componentUpdateStatus, systemUpdate *componentUpd
SystemUpdateAvailableReason: systemUpdate.availableReason,
AppUpdateAvailable: appUpdate.available,
AppUpdateAvailableReason: appUpdate.availableReason,
+ WillDisableAutoUpdate: appUpdate.customVersionUpdate || systemUpdate.customVersionUpdate,
Error: error,
}
}
@@ -180,6 +187,7 @@ type Options struct {
ReleaseAPIEndpoint string
ResetConfig ResetConfigFunc
SkipConfirmSystem bool
+ SetAutoUpdate SetAutoUpdateFunc
}
// NewState creates a new OTA state
@@ -198,6 +206,7 @@ func NewState(opts Options) *State {
componentUpdateStatuses: components,
releaseAPIEndpoint: opts.ReleaseAPIEndpoint,
resetConfig: opts.ResetConfig,
+ setAutoUpdate: opts.SetAutoUpdate,
}
if !opts.SkipConfirmSystem {
go s.confirmCurrentSystem()
diff --git a/ota.go b/ota.go
index 19ef20fd..595933aa 100644
--- a/ota.go
+++ b/ota.go
@@ -31,6 +31,7 @@ func initOta() {
GetLocalVersion: GetLocalVersion,
HwReboot: hwReboot,
ResetConfig: rpcResetConfig,
+ SetAutoUpdate: rpcSetAutoUpdateState,
OnStateUpdate: func(state *ota.RPCState) {
triggerOTAStateUpdate(state)
},
@@ -82,6 +83,7 @@ func getUpdateStatus(includePreRelease bool) (*ota.UpdateStatus, error) {
DeviceID: GetDeviceID(),
IncludePreRelease: includePreRelease,
})
+
// to ensure backwards compatibility,
// if there's an error, we won't return an error, but we will set the error field
if err != nil {
@@ -91,6 +93,11 @@ func getUpdateStatus(includePreRelease bool) (*ota.UpdateStatus, error) {
updateStatus.Error = err.Error()
}
+ // otaState doesn't have the current auto-update state, so we need to get it from the config
+ if updateStatus.WillDisableAutoUpdate {
+ updateStatus.WillDisableAutoUpdate = config.AutoUpdateEnabled
+ }
+
otaLogger.Info().Interface("updateStatus", updateStatus).Msg("Update status")
return updateStatus, nil
diff --git a/ui/localization/messages/da.json b/ui/localization/messages/da.json
index f2773dee..1a41e0cc 100644
--- a/ui/localization/messages/da.json
+++ b/ui/localization/messages/da.json
@@ -302,6 +302,7 @@
"general_update_up_to_date_title": "Systemet er opdateret",
"general_update_updating_description": "Sluk ikke enheden. Denne proces kan tage et par minutter.",
"general_update_updating_title": "Opdatering af din enhed",
+ "general_update_will_disable_auto_update_description": "Du nedgraderer i øjeblikket til en tidligere version. Automatisk opdatering vil blive deaktiveret, når opdateringen er fuldført, for at forhindre utilsigtede opdateringer.",
"getting_remote_session_description": "Henter beskrivelse af fjernsessionsforsøg {attempt}",
"hardware_backlight_settings_error": "Kunne ikke indstille baggrundsbelysningsindstillinger: {error}",
"hardware_backlight_settings_get_error": "Kunne ikke hente indstillinger for baggrundsbelysning: {error}",
diff --git a/ui/localization/messages/de.json b/ui/localization/messages/de.json
index f855ab88..e37b7be0 100644
--- a/ui/localization/messages/de.json
+++ b/ui/localization/messages/de.json
@@ -302,6 +302,7 @@
"general_update_up_to_date_title": "Das System ist auf dem neuesten Stand",
"general_update_updating_description": "Bitte schalten Sie Ihr Gerät nicht aus. Dieser Vorgang kann einige Minuten dauern.",
"general_update_updating_title": "Aktualisieren Ihres Geräts",
+ "general_update_will_disable_auto_update_description": "Sie führen derzeit ein Downgrade auf eine ältere Version durch. Die automatische Aktualisierung wird nach Abschluss des Updates deaktiviert, um versehentliche Aktualisierungen zu verhindern.",
"getting_remote_session_description": "Versuch, eine Beschreibung der Remote-Sitzung abzurufen {attempt}",
"hardware_backlight_settings_error": "Fehler beim Festlegen der Hintergrundbeleuchtungseinstellungen: {error}",
"hardware_backlight_settings_get_error": "Die Einstellungen für die Hintergrundbeleuchtung konnten nicht abgerufen werden: {error}",
diff --git a/ui/localization/messages/en.json b/ui/localization/messages/en.json
index a52d1629..26be6bcf 100644
--- a/ui/localization/messages/en.json
+++ b/ui/localization/messages/en.json
@@ -302,6 +302,7 @@
"general_update_up_to_date_title": "System is up to date",
"general_update_updating_description": "Please don't turn off your device. This process may take a few minutes.",
"general_update_updating_title": "Updating your device",
+ "general_update_will_disable_auto_update_description": "You're currently downgrading to a previous version. Auto-update will be disabled after the update is completed to prevent accidental updates.",
"getting_remote_session_description": "Getting remote session description attempt {attempt}",
"hardware_backlight_settings_error": "Failed to set backlight settings: {error}",
"hardware_backlight_settings_get_error": "Failed to get backlight settings: {error}",
diff --git a/ui/localization/messages/es.json b/ui/localization/messages/es.json
index bb3e5500..d4987c9a 100644
--- a/ui/localization/messages/es.json
+++ b/ui/localization/messages/es.json
@@ -302,6 +302,7 @@
"general_update_up_to_date_title": "El sistema está actualizado",
"general_update_updating_description": "No apagues tu dispositivo. Este proceso puede tardar unos minutos.",
"general_update_updating_title": "Actualizar su dispositivo",
+ "general_update_will_disable_auto_update_description": "Estás instalando una versión anterior. La actualización automática se desactivará una vez finalizada la actualización para evitar actualizaciones accidentales.",
"getting_remote_session_description": "Obtener un intento de descripción de sesión remota {attempt}",
"hardware_backlight_settings_error": "No se pudieron configurar los ajustes de la retroiluminación: {error}",
"hardware_backlight_settings_get_error": "No se pudieron obtener los ajustes de la retroiluminación: {error}",
diff --git a/ui/localization/messages/fr.json b/ui/localization/messages/fr.json
index d44f21b0..2d1f2fe4 100644
--- a/ui/localization/messages/fr.json
+++ b/ui/localization/messages/fr.json
@@ -302,6 +302,7 @@
"general_update_up_to_date_title": "Le système est à jour",
"general_update_updating_description": "Veuillez ne pas éteindre votre appareil. Ce processus peut prendre quelques minutes.",
"general_update_updating_title": "Mise à jour de votre appareil",
+ "general_update_will_disable_auto_update_description": "Vous allez revenir à une version antérieure. La mise à jour automatique sera désactivée une fois l'opération terminée afin d'éviter toute mise à jour accidentelle.",
"getting_remote_session_description": "Obtention d'{attempt} description de session à distance",
"hardware_backlight_settings_error": "Échec de la définition des paramètres de rétroéclairage : {error}",
"hardware_backlight_settings_get_error": "Échec de l'obtention des paramètres de rétroéclairage : {error}",
diff --git a/ui/localization/messages/it.json b/ui/localization/messages/it.json
index 30880f9c..b5d0007b 100644
--- a/ui/localization/messages/it.json
+++ b/ui/localization/messages/it.json
@@ -302,6 +302,7 @@
"general_update_up_to_date_title": "Il sistema è aggiornato",
"general_update_updating_description": "Non spegnere il dispositivo. Questo processo potrebbe richiedere alcuni minuti.",
"general_update_updating_title": "Aggiornamento del dispositivo",
+ "general_update_will_disable_auto_update_description": "Stai eseguendo il downgrade a una versione precedente. L'aggiornamento automatico verrà disattivato al termine dell'aggiornamento per evitare aggiornamenti accidentali.",
"getting_remote_session_description": "Tentativo di ottenimento della descrizione della sessione remota {attempt}",
"hardware_backlight_settings_error": "Impossibile impostare le impostazioni della retroilluminazione: {error}",
"hardware_backlight_settings_get_error": "Impossibile ottenere le impostazioni della retroilluminazione: {error}",
diff --git a/ui/localization/messages/nb.json b/ui/localization/messages/nb.json
index 1e8e1831..6f8b1ba3 100644
--- a/ui/localization/messages/nb.json
+++ b/ui/localization/messages/nb.json
@@ -302,6 +302,7 @@
"general_update_up_to_date_title": "Alt er oppdatert",
"general_update_updating_description": "Ikke slå av enheten. Denne prosessen kan ta noen minutter.",
"general_update_updating_title": "Oppdaterer enheten din",
+ "general_update_will_disable_auto_update_description": "Du nedgraderer for øyeblikket til en tidligere versjon. Automatisk oppdatering vil bli deaktivert etter at oppdateringen er fullført for å forhindre utilsiktede oppdateringer.",
"getting_remote_session_description": "Henter beskrivelse av ekstern øktforsøk {attempt}",
"hardware_backlight_settings_error": "Kunne ikke angi innstillinger for bakgrunnsbelysning: {error}",
"hardware_backlight_settings_get_error": "Klarte ikke å hente bakgrunnsbelysningsinnstillinger: {error}",
diff --git a/ui/localization/messages/sv.json b/ui/localization/messages/sv.json
index 1be9bb79..2af36228 100644
--- a/ui/localization/messages/sv.json
+++ b/ui/localization/messages/sv.json
@@ -302,6 +302,7 @@
"general_update_up_to_date_title": "Systemet är uppdaterat",
"general_update_updating_description": "Stäng inte av enheten. Den här processen kan ta några minuter.",
"general_update_updating_title": "Uppdaterar din enhet",
+ "general_update_will_disable_auto_update_description": "Du nedgraderar för närvarande till en tidigare version. Automatisk uppdatering inaktiveras efter att uppdateringen är klar för att förhindra oavsiktliga uppdateringar.",
"getting_remote_session_description": "Hämtar beskrivning av fjärrsession försök {attempt}",
"hardware_backlight_settings_error": "Misslyckades med att ställa in bakgrundsbelysning: {error}",
"hardware_backlight_settings_get_error": "Misslyckades med att hämta inställningar för bakgrundsbelysning: {error}",
diff --git a/ui/localization/messages/zh.json b/ui/localization/messages/zh.json
index 46ee0b75..cc1cbc35 100644
--- a/ui/localization/messages/zh.json
+++ b/ui/localization/messages/zh.json
@@ -302,6 +302,7 @@
"general_update_up_to_date_title": "系统已更新",
"general_update_updating_description": "正在更新,请勿关闭设备。该过程可能需要数分钟。",
"general_update_updating_title": "更新您的设备",
+ "general_update_will_disable_auto_update_description": "您目前正在降级到之前的版本。更新完成后,自动更新功能将被禁用,以防止意外更新。",
"getting_remote_session_description": "获取远程会话描述尝试 {attempt}",
"hardware_backlight_settings_error": "无法设置背光设置: {error}",
"hardware_backlight_settings_get_error": "无法获取背光设置: {error}",
diff --git a/ui/src/routes/devices.$id.settings.general.update.tsx b/ui/src/routes/devices.$id.settings.general.update.tsx
index c69d2140..cef41f2c 100644
--- a/ui/src/routes/devices.$id.settings.general.update.tsx
+++ b/ui/src/routes/devices.$id.settings.general.update.tsx
@@ -468,6 +468,11 @@ function UpdateAvailableState({
{m.general_update_application_type()}: {versionInfo?.local?.appVersion} → {versionInfo?.remote?.appVersion}
>
) : null}
+ {versionInfo?.willDisableAutoUpdate ? (
+