web/app/canopy/%5Bowner%5D/%5Brepo%5D/builds/%5BrunId%5D/page.tsxblame
View source
1da98741import type { Metadata } from "next";
da0f6512import { PipelineRunDetail } from "@/app/components/pipeline-run-detail";
bc1b2ba3import { getCanopyRun } from "@/lib/grove-api";
da0f6514
1da98745interface Props {
1da98746 params: Promise<{ owner: string; repo: string; runId: string }>;
1da98747}
1da98748
ad0b63b9const statusFaviconColor: Record<string, string> = {
ad0b63b10 pending: "#a09888",
ad0b63b11 running: "#6b4fa0",
ad0b63b12 passed: "#2d6b56",
ad0b63b13 failed: "#a05050",
ad0b63b14 skipped: "#7a746c",
ad0b63b15 cancelled: "#7a746c",
ad0b63b16};
ad0b63b17
ad0b63b18function getCanopyStatusFaviconSvg(color: string): string {
ad0b63b19 return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
ad0b63b20 <circle cx="32" cy="32" r="32" fill="${color}"/>
ad0b63b21 <path d="M31 42 L30 53" stroke="white" stroke-width="2.5" stroke-linecap="round"/>
ad0b63b22 <path d="M30.5 46 L34 49.5" stroke="white" stroke-width="1.5" stroke-linecap="round"/>
ad0b63b23 <path d="M31 42 C18 40, 11 32, 14 24 C16 17, 24 12, 31 14 C33 10, 40 12, 43 16 C48 14, 52 22, 49 28 C52 34, 46 40, 38 40 C36 42, 33 43, 31 42 Z" fill="white"/>
ad0b63b24 </svg>`;
ad0b63b25}
ad0b63b26
1da987427export async function generateMetadata({ params }: Props): Promise<Metadata> {
1da987428 const { owner, repo, runId } = await params;
86450dc29 const title = `Build #${runId} · ${repo}`;
ad0b63b30
ad0b63b31 try {
bc1b2ba32 const data = (await getCanopyRun(owner, repo, runId)) as { run?: { status?: string } } | null;
bc1b2ba33 if (!data) return { title };
ad0b63b34 const status = data.run?.status ?? "";
ad0b63b35 const color = statusFaviconColor[status] ?? "#4d8a78";
ad0b63b36 const svg = getCanopyStatusFaviconSvg(color);
ad0b63b37 return {
ad0b63b38 title,
ad0b63b39 icons: {
ad0b63b40 icon: `data:image/svg+xml,${encodeURIComponent(svg)}`,
ad0b63b41 },
ad0b63b42 };
ad0b63b43 } catch {
ad0b63b44 return { title };
ad0b63b45 }
1da987446}
1da987447
fe3b50948export default async function CanopyBuildRunPage({ params }: Props) {
fe3b50949 const { owner, repo, runId } = await params;
fe3b50950 return <PipelineRunDetail owner={owner} repo={repo} runId={runId} />;
da0f65151}