feat(ota): add kv helpers and policy logic

This commit is contained in:
DIYgod 2026-04-10 19:03:46 +08:00
parent acf0ace77c
commit c7d0fd1eeb
4 changed files with 109 additions and 0 deletions

View File

@ -0,0 +1,27 @@
import { describe, expect, it } from "vitest"
import { evaluateStorePolicy } from "../lib/policy"
import type { OtaRelease } from "../lib/schema"
describe("evaluateStorePolicy", () => {
it("requires a store update when a store release is newer than the installed binary", () => {
const policy = evaluateStorePolicy(
{
releaseVersion: "0.4.3",
releaseKind: "store",
runtimeVersion: "0.4.3",
policy: {
storeRequired: true,
minSupportedBinaryVersion: "0.4.3",
message: "Please update from the store.",
},
} satisfies Pick<OtaRelease, "releaseVersion" | "releaseKind" | "runtimeVersion" | "policy">,
{
installedBinaryVersion: "0.4.1",
},
)
expect(policy.action).toBe("block")
expect(policy.targetVersion).toBe("0.4.3")
})
})

View File

@ -0,0 +1,16 @@
import type { OtaPlatform, OtaRelease } from "./schema"
export const KV_KEYS = {
release: (product: OtaRelease["product"], releaseVersion: OtaRelease["releaseVersion"]) =>
`release:${product}:${releaseVersion}`,
latest: (
product: OtaRelease["product"],
channel: OtaRelease["channel"],
runtimeVersion: OtaRelease["runtimeVersion"],
platform: OtaPlatform,
) => `latest:${product}:${channel}:${runtimeVersion}:${platform}`,
policy: (product: OtaRelease["product"], channel: OtaRelease["channel"]) =>
`policy:${product}:${channel}`,
githubEtag: "github:etag:releases",
syncLastSuccessAt: "sync:last-success-at",
} as const

26
apps/ota/src/lib/kv.ts Normal file
View File

@ -0,0 +1,26 @@
import { KV_KEYS } from "./constants"
import type { OtaPlatform, OtaRelease } from "./schema"
export async function getLatestReleasePointer<T = unknown>(
kv: KVNamespace,
input: {
product: OtaRelease["product"]
channel: OtaRelease["channel"]
runtimeVersion: OtaRelease["runtimeVersion"]
platform: OtaPlatform
},
) {
return kv.get<T>(
KV_KEYS.latest(input.product, input.channel, input.runtimeVersion, input.platform),
"json",
)
}
export async function putReleaseRecord<T>(
kv: KVNamespace,
product: OtaRelease["product"],
releaseVersion: OtaRelease["releaseVersion"],
value: T,
) {
await kv.put(KV_KEYS.release(product, releaseVersion), JSON.stringify(value))
}

View File

@ -0,0 +1,40 @@
import type { OtaRelease } from "./schema"
import { compareSemver } from "./version"
type StoreReleasePolicyInput = Pick<
OtaRelease,
"releaseVersion" | "releaseKind" | "runtimeVersion" | "policy"
>
type StorePolicyEvaluation =
| {
action: "none"
targetVersion: null
message: null
}
| {
action: "block" | "prompt"
targetVersion: OtaRelease["releaseVersion"]
message: string | null
}
export function evaluateStorePolicy(
latestStoreRelease: StoreReleasePolicyInput | null,
input: {
installedBinaryVersion: OtaRelease["releaseVersion"]
},
): StorePolicyEvaluation {
if (!latestStoreRelease || latestStoreRelease.releaseKind !== "store") {
return { action: "none", targetVersion: null, message: null }
}
if (compareSemver(latestStoreRelease.releaseVersion, input.installedBinaryVersion) <= 0) {
return { action: "none", targetVersion: null, message: null }
}
return {
action: latestStoreRelease.policy.storeRequired ? "block" : "prompt",
targetVersion: latestStoreRelease.releaseVersion,
message: latestStoreRelease.policy.message,
}
}