213 lines · 9.0 KB
import { createFileRoute, Link, useNavigate } from "@tanstack/react-router";
import { z } from "zod";
import { approveRun, cancelRun, getRun, rerunRun } from "~/fns/actions";
import { useAction } from "~/hooks/use-action";
import { usePollWhile } from "~/hooks/use-poll-while";
import { JobLog } from "~/components/JobLog";
import { DeploymentGate } from "~/components/DeploymentGate";
import { Badge } from "~/ui/Badge";
import { formatBytes, formatDuration } from "~/lib/format";
import { Panel } from "~/ui/Panel";
import { RelativeTime } from "~/ui/RelativeTime";
import { StatusDot, runState } from "~/ui/StatusDot";
import { Button } from "~/ui/Button";
import { ErrorBanner } from "~/ui/Callout";
import { cn } from "~/utils/cn";
import { Callout } from "~/ui/Callout";
import { ExternalLink, TextLink } from "~/ui/TextLink";
import { Heading } from "~/ui/Heading";

/** A waiting job flips to running within seconds; once its log streams, only the run's final state needs a poll. */
const WAITING_POLL_MS = 2_000;
const RUNNING_POLL_MS = 10_000;

export const Route = createFileRoute("/$owner/$repo/actions/runs/$number")({
  validateSearch: z.object({ job: z.string().optional() }),
  loader: ({ params }) => getRun({ data: { owner: params.owner, repo: params.repo, number: Number(params.number) } }),
  component: RunPage,
});

function RunPage() {
  const data = Route.useLoaderData();
  const params = Route.useParams();
  const search = Route.useSearch();
  const navigate = useNavigate();
  const cancel = useAction(cancelRun);
  const rerun = useAction(rerunRun, { invalidate: false });
  const approve = useAction(approveRun);
  const { run } = data;
  const selected =
    data.jobs.find((job) => job.id === search.job) ??