Skip to content
32 linesCodeBlameRaw
1import { redirect } from "react-router";
2
3import type { Route } from "./+types/actions-artifact";
4import { addresses } from "../../lib/addresses.server";
5import { contentDisposition } from "../../lib/content-safety";
6import { readArtifact } from "../../lib/artifacts.server";
7import { actions } from "../../lib/services.server";
8import { getViewer } from "../../lib/session.server";
9
10/**
11 * One artifact of a run, for anyone who can see the run: a redirect to a
12 * link the API signed for a few minutes, or, for one an older runner kept
13 * in KV, its bytes.
14 */
15export async function loader({ params, context }: Route.LoaderArgs) {
16 const repo = { namespace: params.owner, name: params.repo };
17 const viewer = getViewer(context);
18 const found = await actions.artifactDownload(repo, viewer, { run: params.id, name: params.name });
19 if (found.ok && found.value.blob) {
20 throw redirect(`${addresses().api}/actions/toolkit/blobs/${found.value.blob}`);
21 }
22 const seen = await actions.run(repo, viewer, params.id);
23 if (!seen.ok) throw new Response("Not found.", { status: 404 });
24 const bytes = await readArtifact(params.id, params.name);
25 if (!bytes) throw new Response("That artifact is gone: it expired or was deleted.", { status: 404 });
26 return new Response(bytes.buffer as ArrayBuffer, {
27 headers: {
28 "content-type": "application/gzip",
29 "content-disposition": contentDisposition(`${params.name}.tar.gz`),
30 },
31 });
32}