mirror of https://github.com/jetkvm/kvm.git
fix: update components
This commit is contained in:
parent
2b3f392f0f
commit
f6b0b7297d
|
|
@ -1,5 +0,0 @@
|
|||
package ota
|
||||
|
||||
import "github.com/jetkvm/kvm/internal/logging"
|
||||
|
||||
var logger = logging.GetSubsystemLogger("ota")
|
||||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
|
|
@ -59,6 +60,10 @@ func (s *State) fetchUpdateMetadata(ctx context.Context, params UpdateParams) (*
|
|||
return nil, fmt.Errorf("error getting update URL: %w", err)
|
||||
}
|
||||
|
||||
s.l.Trace().
|
||||
Str("url", url).
|
||||
Msg("fetching update metadata")
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating request: %w", err)
|
||||
|
|
@ -107,6 +112,16 @@ func (s *State) doUpdate(ctx context.Context, params UpdateParams) error {
|
|||
return fmt.Errorf("update already in progress")
|
||||
}
|
||||
|
||||
if len(params.Components) == 0 {
|
||||
params.Components = []string{"app", "system"}
|
||||
}
|
||||
shouldUpdateApp := slices.Contains(params.Components, "app")
|
||||
shouldUpdateSystem := slices.Contains(params.Components, "system")
|
||||
|
||||
if !shouldUpdateApp && !shouldUpdateSystem {
|
||||
return fmt.Errorf("no components to update")
|
||||
}
|
||||
|
||||
if !params.CheckOnly {
|
||||
s.updating = true
|
||||
s.triggerStateUpdate()
|
||||
|
|
@ -128,12 +143,12 @@ func (s *State) doUpdate(ctx context.Context, params UpdateParams) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
if appUpdate.available || appUpdate.downgradeAvailable {
|
||||
if shouldUpdateApp && (appUpdate.available || appUpdate.downgradeAvailable) {
|
||||
appUpdate.pending = true
|
||||
s.triggerComponentUpdateState("app", appUpdate)
|
||||
}
|
||||
|
||||
if systemUpdate.available || systemUpdate.downgradeAvailable {
|
||||
if shouldUpdateSystem && (systemUpdate.available || systemUpdate.downgradeAvailable) {
|
||||
systemUpdate.pending = true
|
||||
s.triggerComponentUpdateState("system", systemUpdate)
|
||||
}
|
||||
|
|
@ -180,6 +195,7 @@ type UpdateParams struct {
|
|||
DeviceID string `json:"deviceID"`
|
||||
AppTargetVersion string `json:"appTargetVersion"`
|
||||
SystemTargetVersion string `json:"systemTargetVersion"`
|
||||
Components []string `json:"components,omitempty"`
|
||||
IncludePreRelease bool `json:"includePreRelease"`
|
||||
CheckOnly bool `json:"checkOnly"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ type componentUpdateStatus struct {
|
|||
verifiedAt time.Time
|
||||
updateProgress float32
|
||||
updatedAt time.Time
|
||||
dependsOn []string
|
||||
dependsOn []string //nolint:unused
|
||||
}
|
||||
|
||||
// RPCState represents the current OTA state for the RPC API
|
||||
|
|
|
|||
|
|
@ -84,6 +84,8 @@ func (s *State) updateSystem(ctx context.Context, systemUpdate *componentUpdateS
|
|||
return s.componentUpdateError("Error executing rk_ota command", err, &rkLogger)
|
||||
}
|
||||
rkLogger.Info().Msg("rk_ota success")
|
||||
|
||||
s.rebootNeeded = true
|
||||
systemUpdate.updateProgress = 1
|
||||
systemUpdate.updatedAt = verifyFinished
|
||||
s.triggerComponentUpdateState("system", systemUpdate)
|
||||
|
|
|
|||
8
ota.go
8
ota.go
|
|
@ -137,6 +137,7 @@ func rpcGetLocalVersion() (*ota.LocalMetadata, error) {
|
|||
type tryUpdateComponents struct {
|
||||
AppTargetVersion string `json:"app"`
|
||||
SystemTargetVersion string `json:"system"`
|
||||
Components string `json:"components,omitempty"` // components is a comma-separated list of components to update
|
||||
}
|
||||
|
||||
func rpcTryUpdate() error {
|
||||
|
|
@ -155,17 +156,18 @@ func rpcTryUpdateComponents(components tryUpdateComponents, includePreRelease bo
|
|||
|
||||
logger.Info().Interface("components", components).Msg("components")
|
||||
|
||||
if components.AppTargetVersion != "" {
|
||||
updateParams.AppTargetVersion = components.AppTargetVersion
|
||||
if err := otaState.SetTargetVersion("app", components.AppTargetVersion); err != nil {
|
||||
return fmt.Errorf("failed to set app target version: %w", err)
|
||||
}
|
||||
}
|
||||
if components.SystemTargetVersion != "" {
|
||||
|
||||
updateParams.SystemTargetVersion = components.SystemTargetVersion
|
||||
if err := otaState.SetTargetVersion("system", components.SystemTargetVersion); err != nil {
|
||||
return fmt.Errorf("failed to set system target version: %w", err)
|
||||
}
|
||||
|
||||
if components.Components != "" {
|
||||
updateParams.Components = strings.Split(components.Components, ",")
|
||||
}
|
||||
|
||||
go func() {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { useCallback, useMemo } from "react";
|
||||
import semver from "semver";
|
||||
|
||||
import { useDeviceStore } from "@/hooks/stores";
|
||||
import { JsonRpcError, RpcMethodNotFound } from "@/hooks/useJsonRpc";
|
||||
import { getUpdateStatus, getLocalVersion as getLocalVersionRpc } from "@/utils/jsonrpc";
|
||||
import notifications from "@/notifications";
|
||||
import { m } from "@localizations/messages.js";
|
||||
import semver from "semver";
|
||||
|
||||
export interface VersionInfo {
|
||||
appVersion: string;
|
||||
|
|
|
|||
|
|
@ -197,10 +197,14 @@ export default function SettingsAdvancedRoute() {
|
|||
);
|
||||
return;
|
||||
}
|
||||
const pageParams = new URLSearchParams();
|
||||
pageParams.set("downgrade", "true");
|
||||
pageParams.set("components", updateTarget == "both" ? "app,system" : updateTarget);
|
||||
|
||||
// Navigate to update page
|
||||
navigateTo("/settings/general/update");
|
||||
navigateTo(`/settings/general/update?${pageParams.toString()}`);
|
||||
});
|
||||
}, [appVersion, systemVersion, devChannel, send, navigateTo]);
|
||||
}, [updateTarget,appVersion, systemVersion, devChannel, send, navigateTo]);
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useLocation, useNavigate } from "react-router";
|
||||
import { useLocation, useNavigate, useSearchParams } from "react-router";
|
||||
|
||||
import { useJsonRpc } from "@hooks/useJsonRpc";
|
||||
import { UpdateState, useUpdateStore } from "@hooks/stores";
|
||||
|
|
@ -16,11 +16,16 @@ import { SystemVersionInfo } from "@/utils/jsonrpc";
|
|||
export default function SettingsGeneralUpdateRoute() {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
//@ts-ignore
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const { updateSuccess } = location.state || {};
|
||||
|
||||
const { setModalView, otaState } = useUpdateStore();
|
||||
const { send } = useJsonRpc();
|
||||
|
||||
const downgrade = useMemo(() => searchParams.get("downgrade") === "true", [searchParams]);
|
||||
const updateComponents = useMemo(() => searchParams.get("components") || "", [searchParams]);
|
||||
|
||||
const onClose = useCallback(async () => {
|
||||
navigate(".."); // back to the devices.$id.settings page
|
||||
// Add 1s delay between navigation and calling reload() to prevent reload from interrupting the navigation.
|
||||
|
|
@ -33,6 +38,18 @@ export default function SettingsGeneralUpdateRoute() {
|
|||
setModalView("updating");
|
||||
}, [send, setModalView]);
|
||||
|
||||
const onConfirmDowngrade = useCallback((system?: string, app?: string) => {
|
||||
send("tryUpdateComponents", {
|
||||
components: {
|
||||
system, app,
|
||||
components: updateComponents
|
||||
},
|
||||
includePreRelease: true,
|
||||
checkOnly: false,
|
||||
});
|
||||
setModalView("updating");
|
||||
}, [send, setModalView, updateComponents]);
|
||||
|
||||
useEffect(() => {
|
||||
if (otaState.updating) {
|
||||
setModalView("updating");
|
||||
|
|
@ -45,15 +62,24 @@ export default function SettingsGeneralUpdateRoute() {
|
|||
}
|
||||
}, [otaState.error, otaState.updating, setModalView, updateSuccess]);
|
||||
|
||||
return <Dialog onClose={onClose} onConfirmUpdate={onConfirmUpdate} />;
|
||||
return <Dialog
|
||||
onClose={onClose}
|
||||
onConfirmUpdate={onConfirmUpdate}
|
||||
onConfirmDowngrade={onConfirmDowngrade}
|
||||
downgrade={downgrade}
|
||||
/>;
|
||||
}
|
||||
|
||||
export function Dialog({
|
||||
onClose,
|
||||
onConfirmUpdate,
|
||||
onConfirmDowngrade,
|
||||
downgrade,
|
||||
}: Readonly<{
|
||||
downgrade: boolean;
|
||||
onClose: () => void;
|
||||
onConfirmUpdate: () => void;
|
||||
onConfirmDowngrade: () => void;
|
||||
}>) {
|
||||
const { navigateTo } = useDeviceUiNavigation();
|
||||
|
||||
|
|
@ -70,15 +96,15 @@ export function Dialog({
|
|||
|
||||
setVersionInfo(versionInfo);
|
||||
|
||||
if (hasUpdate) {
|
||||
setModalView("updateAvailable");
|
||||
} else if (hasDowngrade) {
|
||||
if (hasDowngrade && downgrade) {
|
||||
setModalView("updateDowngradeAvailable");
|
||||
} else if (hasUpdate) {
|
||||
setModalView("updateAvailable");
|
||||
} else {
|
||||
setModalView("upToDate");
|
||||
}
|
||||
},
|
||||
[setModalView],
|
||||
[setModalView, downgrade],
|
||||
);
|
||||
|
||||
const onCancelDowngrade = useCallback(() => {
|
||||
|
|
@ -110,7 +136,7 @@ export function Dialog({
|
|||
)}
|
||||
{modalView === "updateDowngradeAvailable" && (
|
||||
<UpdateDowngradeAvailableState
|
||||
onConfirmUpdate={onConfirmUpdate}
|
||||
onConfirmDowngrade={onConfirmDowngrade}
|
||||
onCancelDowngrade={onCancelDowngrade}
|
||||
versionInfo={versionInfo!}
|
||||
/>
|
||||
|
|
@ -429,13 +455,19 @@ function UpdateAvailableState({
|
|||
|
||||
function UpdateDowngradeAvailableState({
|
||||
versionInfo,
|
||||
onConfirmUpdate,
|
||||
onConfirmDowngrade,
|
||||
onCancelDowngrade,
|
||||
}: {
|
||||
versionInfo: SystemVersionInfo;
|
||||
onConfirmUpdate: () => void;
|
||||
onConfirmDowngrade: (system?: string, app?: string) => void;
|
||||
onCancelDowngrade: () => void;
|
||||
}) {
|
||||
const confirmDowngrade = useCallback(() => {
|
||||
onConfirmDowngrade(
|
||||
versionInfo?.remote?.systemVersion || undefined,
|
||||
versionInfo?.remote?.appVersion || undefined,
|
||||
);
|
||||
}, [versionInfo, onConfirmDowngrade]);
|
||||
return (
|
||||
<div className="flex flex-col items-start justify-start space-y-4 text-left">
|
||||
<div className="text-left">
|
||||
|
|
@ -459,7 +491,7 @@ function UpdateDowngradeAvailableState({
|
|||
) : null}
|
||||
</p>
|
||||
<div className="flex items-center justify-start gap-x-2">
|
||||
<Button size="SM" theme="primary" text={m.general_update_downgrade_button()} onClick={onConfirmUpdate} />
|
||||
<Button size="SM" theme="primary" text={m.general_update_downgrade_button()} onClick={confirmDowngrade} />
|
||||
<Button size="SM" theme="light" text={m.general_update_keep_current_button()} onClick={onCancelDowngrade} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue