diff --git a/apps/web/src/lib/types/releases.ts b/apps/web/src/lib/types/releases.ts index ca2a2c8a03..ff407b6ec0 100644 --- a/apps/web/src/lib/types/releases.ts +++ b/apps/web/src/lib/types/releases.ts @@ -6,12 +6,54 @@ export interface Build { platform: string; } +export function isReleaseBuild(something: unknown): something is Build { + return ( + typeof something === 'object' && + something !== null && + typeof (something as any).os === 'string' && + typeof (something as any).arch === 'string' && + typeof (something as any).url === 'string' && + typeof (something as any).file === 'string' && + typeof (something as any).platform === 'string' + ); +} + export interface Release { version: string; - notes: string; + notes: string | null; sha: string; channel: 'release' | string; build_version: string; released_at: string; builds: Build[]; } + +export function isRelease(something: unknown): something is Release { + return ( + typeof something === 'object' && + something !== null && + typeof (something as any).version === 'string' && + (typeof (something as any).notes === 'string' || (something as any).notes === null) && + typeof (something as any).sha === 'string' && + typeof (something as any).channel === 'string' && + typeof (something as any).build_version === 'string' && + typeof (something as any).released_at === 'string' && + Array.isArray((something as any).builds) && + (something as any).builds.every(isReleaseBuild) + ); +} + +export function getValidReleases(something: unknown): Release[] { + if (!Array.isArray(something)) return []; + const result: Release[] = []; + for (const item of something) { + if (isRelease(item)) { + result.push(item); + continue; + } + + // TODO: Add some metrics to this + console.warn('Invalid release:', item); + } + return result; +} diff --git a/apps/web/src/routes/downloads/+page.ts b/apps/web/src/routes/downloads/+page.ts index a5819b153a..53de73c1c0 100644 --- a/apps/web/src/routes/downloads/+page.ts +++ b/apps/web/src/routes/downloads/+page.ts @@ -1,4 +1,4 @@ -import type { Build, Release } from '$lib/types/releases'; +import { getValidReleases, type Build, type Release } from '$lib/types/releases'; import type { PageLoad } from './$types'; function processBuilds(builds: Build[]) { @@ -26,7 +26,7 @@ export const load: PageLoad = async () => { const releaseResponse = await fetch( 'https://gitbutler.com/api/downloads?limit=4&channel=release' ); - releases = await releaseResponse.json(); + releases = getValidReleases(await releaseResponse.json()); const latestRelease = releases[0]; releases.forEach((release) => { @@ -45,7 +45,7 @@ export const load: PageLoad = async () => { const nightlyResponse = await fetch( 'https://gitbutler.com/api/downloads?limit=9&channel=nightly' ); - nightlies = await nightlyResponse.json(); + nightlies = getValidReleases(await nightlyResponse.json()); nightlies.forEach((nightlyRelease) => { nightlyRelease.builds = processBuilds(nightlyRelease.builds); });