Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| Merge Actions runs: summaries, attempts and re-runs, graceful cancel, log downloads, badges (actions 0009) | 1 | /** |
| 2 | * A workflow's status badge: an SVG saying how its newest finished run went, | |
| 3 | * on `?branch=` (the default branch, else any) and `?event=` when given. | |
| 4 | * Anyone may load a public repository's; a private repository's needs | |
| 5 | * someone who can see it, and is never cached where others could read it. | |
| 6 | */ | |
| 7 | import type { Route } from "./+types/actions-badge"; | |
| 8 | import { badgeState, badgeSvg } from "../../lib/badge"; | |
| 9 | import { actions, repos } from "../../lib/services.server"; | |
| 10 | import { getViewer } from "../../lib/session.server"; | |
| 11 | ||
| 12 | function svg(body: string, isPrivate: boolean, status = 200): Response { | |
| 13 | return new Response(body, { | |
| 14 | status, | |
| 15 | headers: { | |
| 16 | "content-type": "image/svg+xml; charset=utf-8", | |
| 17 | // Shown in READMEs elsewhere, which fetch it through their own caches. | |
| 18 | "cache-control": isPrivate ? "private, no-store" : "public, max-age=60, s-maxage=60", | |
| 19 | "x-content-type-options": "nosniff", | |
| 20 | "content-security-policy": "default-src 'none'; style-src 'unsafe-inline'", | |
| 21 | }, | |
| 22 | }); | |
| 23 | } | |
| 24 | ||
| 25 | export async function loader({ params, context, request }: Route.LoaderArgs) { | |
| 26 | const viewer = getViewer(context); | |
| 27 | const path = { namespace: params.owner, name: params.repo }; | |
| 28 | const query = new URL(request.url).searchParams; | |
| 29 | const branch = query.get("branch")?.trim() || undefined; | |
| 30 | const event = query.get("event")?.trim() || undefined; | |
| 31 | const repo = await repos.get(path, viewer).catch(() => null); | |
| 32 | // Not there, or not this viewer's to see: the same answer either way. | |
| 33 | if (!repo?.ok) return new Response("Not found.\n", { status: 404, headers: { "content-type": "text/plain; charset=utf-8", "cache-control": "no-store" } }); | |
| 34 | const isPrivate = repo.value.isPrivate; | |
| 35 | const workflows = await actions.workflows(path, viewer); | |
| 36 | const file = params.file; | |
| 37 | const workflow = workflows.ok ? workflows.value.find((w) => w.path.endsWith(`/${file}`) || w.id === file) : undefined; | |
| 38 | if (!workflow) return svg(badgeSvg(file, "no status"), isPrivate, 404); | |
| 39 | const runsOn = async (onBranch: string | undefined) => { | |
| 40 | const runs = await actions.runs(path, viewer, { workflow: workflow.id, branch: onBranch, event, limit: 20 }); | |
| 41 | return runs.ok ? runs.value : []; | |
| 42 | }; | |
| 43 | // The default branch unless one is asked for; any branch when it has none. | |
| 44 | let runs = await runsOn(branch ?? repo.value.defaultBranch); | |
| 45 | if (!branch && !runs.some((run) => run.status === "completed")) runs = await runsOn(undefined); | |
| 46 | return svg(badgeSvg(workflow.name, badgeState(runs)), isPrivate); | |
| 47 | } |
This file's history is long; its oldest lines are credited to the oldest commit read.