1.9 KB57 lines
Blame
1import type { Metadata } from "next";
2import { PipelineRunDetail } from "@/app/components/pipeline-run-detail";
3import { groveApiUrl } from "@/lib/utils";
4
5interface Props {
6 params: Promise<{ owner: string; repo: string; runId: string }>;
7}
8
9const statusFaviconColor: Record<string, string> = {
10 pending: "#a09888",
11 running: "#6b4fa0",
12 passed: "#2d6b56",
13 failed: "#a05050",
14 skipped: "#7a746c",
15 cancelled: "#7a746c",
16};
17
18function getCanopyStatusFaviconSvg(color: string): string {
19 return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
20 <circle cx="32" cy="32" r="32" fill="${color}"/>
21 <path d="M31 42 L30 53" stroke="white" stroke-width="2.5" stroke-linecap="round"/>
22 <path d="M30.5 46 L34 49.5" stroke="white" stroke-width="1.5" stroke-linecap="round"/>
23 <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"/>
24 </svg>`;
25}
26
27export async function generateMetadata({ params }: Props): Promise<Metadata> {
28 const { owner, repo, runId } = await params;
29 const title = `Build #${runId} · ${repo}`;
30
31 try {
32 const res = await fetch(
33 `${groveApiUrl}/api/repos/${owner}/${repo}/canopy/runs/${runId}`,
34 { cache: "no-store" }
35 );
36 if (!res.ok) return { title };
37
38 const data = (await res.json()) as { run?: { status?: string } };
39 const status = data.run?.status ?? "";
40 const color = statusFaviconColor[status] ?? "#4d8a78";
41 const svg = getCanopyStatusFaviconSvg(color);
42 return {
43 title,
44 icons: {
45 icon: `data:image/svg+xml,${encodeURIComponent(svg)}`,
46 },
47 };
48 } catch {
49 return { title };
50 }
51}
52
53export default async function CanopyBuildRunPage({ params }: Props) {
54 const { owner, repo, runId } = await params;
55 return <PipelineRunDetail owner={owner} repo={repo} runId={runId} />;
56}
57