fix: backfill github version badges
This commit is contained in:
parent
7d592abda7
commit
cebce6d9c8
|
|
@ -1987,6 +1987,121 @@ describe("/versions", () => {
|
|||
},
|
||||
})
|
||||
})
|
||||
|
||||
it("backfills latest github release versions when the summary keys are missing", async () => {
|
||||
const mobileRelease = await createReleaseMetadata({
|
||||
releaseVersion: "0.4.3",
|
||||
releaseKind: "store",
|
||||
runtimeVersion: "0.4.3",
|
||||
publishedAt: "2026-04-10T16:00:00Z",
|
||||
git: {
|
||||
tag: "mobile/v0.4.3",
|
||||
commit: "abcdef1234567895",
|
||||
},
|
||||
platforms: {},
|
||||
})
|
||||
const desktopRelease = createDesktopReleaseMetadata({
|
||||
releaseVersion: "1.5.2",
|
||||
releaseKind: "binary",
|
||||
runtimeVersion: null,
|
||||
publishedAt: "2026-04-11T12:00:00Z",
|
||||
git: {
|
||||
tag: "desktop/v1.5.2",
|
||||
commit: "abcdef1234567891",
|
||||
},
|
||||
desktop: {
|
||||
renderer: null,
|
||||
app: null,
|
||||
},
|
||||
})
|
||||
const kvEntries = new Map<string, unknown>([
|
||||
[
|
||||
KV_KEYS.storeVersion("mobile", "ios"),
|
||||
{
|
||||
version: "0.4.1",
|
||||
fetchedAt: "2026-04-12T14:25:27.016Z",
|
||||
source: "app-store",
|
||||
},
|
||||
],
|
||||
])
|
||||
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn(async (input: string | URL | Request) => {
|
||||
const url = String(input)
|
||||
|
||||
if (url === "https://api.github.com/repos/RSSNext/Folo/releases") {
|
||||
return new Response(
|
||||
JSON.stringify([
|
||||
createGitHubReleaseAssetSet(
|
||||
"desktop/v1.5.2",
|
||||
"https://example.com/desktop.json",
|
||||
null,
|
||||
),
|
||||
createGitHubReleaseAssetSet("mobile/v0.4.3", "https://example.com/mobile.json", null),
|
||||
]),
|
||||
{ status: 200 },
|
||||
)
|
||||
}
|
||||
|
||||
if (url === "https://example.com/mobile.json") {
|
||||
return new Response(JSON.stringify(mobileRelease), {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
})
|
||||
}
|
||||
|
||||
if (url === "https://example.com/desktop.json") {
|
||||
return new Response(JSON.stringify(desktopRelease), {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
})
|
||||
}
|
||||
|
||||
throw new Error(`Unhandled fetch URL: ${url}`)
|
||||
}),
|
||||
)
|
||||
|
||||
const response = await fetchWorker("/versions", undefined, {
|
||||
kvEntries,
|
||||
envOverrides: {
|
||||
GITHUB_OWNER: "RSSNext",
|
||||
GITHUB_REPO: "Folo",
|
||||
GITHUB_TOKEN: "token",
|
||||
},
|
||||
})
|
||||
|
||||
expect(response.status).toBe(200)
|
||||
await expect(response.json()).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
github: {
|
||||
mobile: {
|
||||
version: "0.4.3",
|
||||
publishedAt: "2026-04-10T16:00:00Z",
|
||||
tag: "mobile/v0.4.3",
|
||||
url: "https://github.com/RSSNext/Folo/releases/tag/mobile/v0.4.3",
|
||||
},
|
||||
desktop: {
|
||||
version: "1.5.2",
|
||||
publishedAt: "2026-04-11T12:00:00Z",
|
||||
tag: "desktop/v1.5.2",
|
||||
url: "https://github.com/RSSNext/Folo/releases/tag/desktop/v1.5.2",
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
expect(JSON.parse(String(kvEntries.get(KV_KEYS.latestReleaseVersion("mobile"))))).toEqual({
|
||||
product: "mobile",
|
||||
version: "0.4.3",
|
||||
publishedAt: "2026-04-10T16:00:00Z",
|
||||
tag: "mobile/v0.4.3",
|
||||
})
|
||||
expect(JSON.parse(String(kvEntries.get(KV_KEYS.latestReleaseVersion("desktop"))))).toEqual({
|
||||
product: "desktop",
|
||||
version: "1.5.2",
|
||||
publishedAt: "2026-04-11T12:00:00Z",
|
||||
tag: "desktop/v1.5.2",
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("latest release summary", () => {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,20 @@
|
|||
import { Hono } from "hono"
|
||||
|
||||
import type { Env } from "../env"
|
||||
import { getLatestReleaseVersionRecord, getStoreVersionRecord } from "../lib/kv"
|
||||
import { listPublishedOtaReleases } from "../lib/github"
|
||||
import {
|
||||
getLatestReleaseVersionRecord,
|
||||
getStoreVersionRecord,
|
||||
putLatestReleaseVersionRecord,
|
||||
} from "../lib/kv"
|
||||
import { otaReleaseSchema } from "../lib/schema"
|
||||
import { STORE_URLS } from "../lib/store-version"
|
||||
import { compareSemver } from "../lib/version"
|
||||
|
||||
export const versionsRoute = new Hono<{ Bindings: Env }>()
|
||||
|
||||
versionsRoute.get("/versions", async (c) => {
|
||||
const [ios, android, mas, mss, mobileRelease, desktopRelease] = await Promise.all([
|
||||
const [ios, android, mas, mss, cachedMobileRelease, cachedDesktopRelease] = await Promise.all([
|
||||
getStoreVersionRecord(c.env.OTA_KV, { product: "mobile", target: "ios" }),
|
||||
getStoreVersionRecord(c.env.OTA_KV, { product: "mobile", target: "android" }),
|
||||
getStoreVersionRecord(c.env.OTA_KV, { product: "desktop", target: "mas" }),
|
||||
|
|
@ -15,6 +22,14 @@ versionsRoute.get("/versions", async (c) => {
|
|||
getLatestReleaseVersionRecord(c.env.OTA_KV, "mobile"),
|
||||
getLatestReleaseVersionRecord(c.env.OTA_KV, "desktop"),
|
||||
])
|
||||
let mobileRelease = cachedMobileRelease
|
||||
let desktopRelease = cachedDesktopRelease
|
||||
|
||||
if (!mobileRelease || !desktopRelease) {
|
||||
const latestReleaseRecords = await backfillLatestReleaseVersions(c.env)
|
||||
mobileRelease = mobileRelease ?? latestReleaseRecords.mobile
|
||||
desktopRelease = desktopRelease ?? latestReleaseRecords.desktop
|
||||
}
|
||||
|
||||
return c.json({
|
||||
store: {
|
||||
|
|
@ -34,6 +49,66 @@ versionsRoute.get("/versions", async (c) => {
|
|||
})
|
||||
})
|
||||
|
||||
async function backfillLatestReleaseVersions(env: Env) {
|
||||
const releasesResult = await listPublishedOtaReleases({
|
||||
owner: env.GITHUB_OWNER,
|
||||
repo: env.GITHUB_REPO,
|
||||
token: env.GITHUB_TOKEN,
|
||||
etag: null,
|
||||
})
|
||||
|
||||
if (releasesResult.kind === "not-modified") {
|
||||
return {
|
||||
mobile: null,
|
||||
desktop: null,
|
||||
}
|
||||
}
|
||||
|
||||
const latestByProduct = new Map<
|
||||
"mobile" | "desktop",
|
||||
{
|
||||
product: "mobile" | "desktop"
|
||||
version: string
|
||||
publishedAt: string
|
||||
tag: string
|
||||
}
|
||||
>()
|
||||
|
||||
for (const releaseSummary of releasesResult.releases) {
|
||||
const response = await fetch(releaseSummary.metadataUrl)
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch release metadata (${response.status})`)
|
||||
}
|
||||
|
||||
const release = otaReleaseSchema.parse(await response.json())
|
||||
const current = latestByProduct.get(release.product)
|
||||
if (!current || compareSemver(release.releaseVersion, current.version) > 0) {
|
||||
latestByProduct.set(release.product, {
|
||||
product: release.product,
|
||||
version: release.releaseVersion,
|
||||
publishedAt: release.publishedAt,
|
||||
tag: release.git.tag,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const mobile = latestByProduct.get("mobile") ?? null
|
||||
const desktop = latestByProduct.get("desktop") ?? null
|
||||
|
||||
if (mobile) {
|
||||
await putLatestReleaseVersionRecord(env.OTA_KV, mobile)
|
||||
}
|
||||
|
||||
if (desktop) {
|
||||
await putLatestReleaseVersionRecord(env.OTA_KV, desktop)
|
||||
}
|
||||
|
||||
return {
|
||||
mobile,
|
||||
desktop,
|
||||
}
|
||||
}
|
||||
|
||||
function toStoreVersionPayload(
|
||||
record: Awaited<ReturnType<typeof getStoreVersionRecord>>,
|
||||
url: string,
|
||||
|
|
|
|||
Loading…
Reference in New Issue