diff --git a/web/src/lib/api/commitStatuses.ts b/web/src/lib/api/commitStatuses.ts new file mode 100644 index 00000000..38d9024b --- /dev/null +++ b/web/src/lib/api/commitStatuses.ts @@ -0,0 +1,26 @@ +import { browser } from "$app/environment"; +import type { PipelineSummary } from "$lib/components/repo/types"; + +export type CommitStatuses = Record; + +const never = new Promise(() => {}); + +export const commitStatuses = ( + spindle: string | undefined, + ownerHandle: string, + repoName: string, + shas: string[] +): Promise | undefined => { + // only repos with a spindle run CI + if (!spindle || shas.length === 0) return undefined; + if (!browser) return never; + + const params = new URLSearchParams(); + for (const sha of new Set(shas)) params.append("sha", sha); + const path = `/${encodeURIComponent(ownerHandle)}/${encodeURIComponent(repoName)}/commit-statuses?${params}`; + return ( + fetch(path) + .then((res) => (res.ok ? (res.json() as Promise) : ({} as CommitStatuses))) + .catch(() => ({})) + ); +}; diff --git a/web/src/routes/[handle]/[repo]/commit-statuses/+server.ts b/web/src/routes/[handle]/[repo]/commit-statuses/+server.ts new file mode 100644 index 00000000..8438a00f --- /dev/null +++ b/web/src/routes/[handle]/[repo]/commit-statuses/+server.ts @@ -0,0 +1,33 @@ +import { json } from "@sveltejs/kit"; +import { createSpindleClient, queryPipelines, toPipelineSummary } from "$lib/api/spindle"; +import { resolveRepoFromParams } from "$lib/server/repo"; +import type { CommitStatuses } from "$lib/api/commitStatuses"; +import type { RequestHandler } from "./$types"; + +export const GET: RequestHandler = async (event) => { + const { view } = await resolveRepoFromParams(event); + const { spindle, repoDid } = view.value; + + const shas = [...new Set(event.url.searchParams.getAll("sha"))].slice(0, 250); + if (!spindle || !repoDid || shas.length === 0) return json({}); + + const ctx = createSpindleClient(spindle, event.fetch); + const page = await queryPipelines(ctx, { + repo: repoDid, + commits: shas, + limit: shas.length + }).catch(() => null); + // don't fail whole commit list page if we fail statuses + if (!page) return json({}); + + const statuses: CommitStatuses = {}; + // the lexicon says pipelines is required, but the spindle sends null for an + // empty result + for (const pipeline of page.pipelines ?? []) { + // a spindle answering for another repo is misconfigured or lying + if (pipeline.repo !== repoDid) continue; + const summary = toPipelineSummary(pipeline, repoDid); + statuses[summary.sha] = summary; + } + return json(statuses); +};