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