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";
ad0b63b3import { groveApiUrl } from "@/lib/utils";
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 {
ad0b63b32 const res = await fetch(
ad0b63b33 `${groveApiUrl}/api/repos/${owner}/${repo}/canopy/runs/${runId}`,
ad0b63b34 { cache: "no-store" }
ad0b63b35 );
ad0b63b36 if (!res.ok) return { title };
ad0b63b37
ad0b63b38 const data = (await res.json()) as { run?: { status?: string } };
ad0b63b39 const status = data.run?.status ?? "";
ad0b63b40 const color = statusFaviconColor[status] ?? "#4d8a78";
ad0b63b41 const svg = getCanopyStatusFaviconSvg(color);
ad0b63b42 return {
ad0b63b43 title,
ad0b63b44 icons: {
ad0b63b45 icon: `data:image/svg+xml,${encodeURIComponent(svg)}`,
ad0b63b46 },
ad0b63b47 };
ad0b63b48 } catch {
ad0b63b49 return { title };
ad0b63b50 }
1da987451}
1da987452
fe3b50953export default async function CanopyBuildRunPage({ params }: Props) {
fe3b50954 const { owner, repo, runId } = await params;
fe3b50955 return <PipelineRunDetail owner={owner} repo={repo} runId={runId} />;
da0f65156}