2.9 KB98 lines
Blame
1import type { Metadata } from "next";
2import { redirect } from "next/navigation";
3import { getCanopyRunsForRepo } from "@/lib/grove-api";
4import {
5 formatTriggerType,
6 getInvocationRunSlug,
7 getSeedRunIdFromInvocationSlug,
8 getInvocationStatus,
9 groupByInvocation,
10} from "@/lib/canopy-invocations";
11import { InvocationDetail } from "./invocation-detail";
12import { CanopyLiveRefresh } from "@/app/components/canopy-live-refresh";
13
14interface Props {
15 params: Promise<{ owner: string; repo: string; runSlug: string }>;
16}
17
18interface Run {
19 id: number;
20 pipeline_name: string;
21 status: string;
22 trigger_type?: string | null;
23 commit_id: string | null;
24 commit_message: string | null;
25 trigger_ref?: string | null;
26 started_at?: string | null;
27 duration_ms?: number | null;
28 created_at: string;
29 repo_name: string;
30 owner_name: string;
31}
32
33export async function generateMetadata({ params }: Props): Promise<Metadata> {
34 const { repo } = await params;
35 return { title: `Run · ${repo}` };
36}
37
38async function getRuns(owner: string, repo: string): Promise<Run[]> {
39 const data = await getCanopyRunsForRepo<Run>(owner, repo, { limit: 200 });
40 return data?.runs ?? [];
41}
42
43export default async function CanopyInvocationPage({ params }: Props) {
44 const { owner, repo, runSlug } = await params;
45 const normalizedRunSlug = runSlug.toLowerCase();
46 const seedId = getSeedRunIdFromInvocationSlug(normalizedRunSlug);
47 const runs = await getRuns(owner, repo);
48 const invocations = groupByInvocation(runs);
49 const invocation =
50 (seedId !== null
51 ? invocations.find((group) => group.runs.some((run) => run.id === seedId))
52 : null) ??
53 invocations.find((group) => {
54 if (group.commitId) {
55 return group.commitId.toLowerCase().startsWith(normalizedRunSlug);
56 }
57 return getInvocationRunSlug(group, invocations) === normalizedRunSlug;
58 });
59
60 if (!invocation) {
61 return (
62 <div className="px-4 sm:px-6 py-6">
63 <p className="text-sm" style={{ color: "var(--text-faint)" }}>
64 Build invocation not found.
65 </p>
66 </div>
67 );
68 }
69
70 const title =
71 invocation.commitMessage ||
72 (invocation.commitId
73 ? invocation.commitId.substring(0, 8)
74 : `${formatTriggerType(invocation.triggerType)} build`);
75 const status = getInvocationStatus(invocation.runs);
76 const newestRun = invocation.runs[invocation.runs.length - 1];
77 const canonicalSlug = getInvocationRunSlug(invocation, invocations);
78 if (normalizedRunSlug !== canonicalSlug) {
79 redirect(`/${owner}/${repo}/runs/${canonicalSlug}`);
80 }
81
82 return (
83 <>
84 <CanopyLiveRefresh scope="repo" owner={owner} repo={repo} />
85 <InvocationDetail
86 owner={owner}
87 repo={repo}
88 runs={invocation.runs}
89 title={title}
90 status={status}
91 triggerType={invocation.triggerType ?? null}
92 commitId={invocation.commitId}
93 newestCreatedAt={newestRun.created_at}
94 />
95 </>
96 );
97}
98