"use server"; import { revalidatePath } from "next/cache"; import { redirect } from "next/navigation"; import { person } from "@/lib/actor"; import { createFork, syncFork } from "@/lib/forks"; import { GitError } from "@/lib/git"; import { ProtectionError } from "@/lib/protection"; import { RepoError } from "@/lib/repos"; import { ActionError, repoForAction } from "./guard"; import type { FormState } from "./types"; // Anyone who can read a repository may fork it; syncing a fork needs push access to the fork. const isUserError = (e: unknown) => e instanceof ActionError || e instanceof RepoError || e instanceof GitError || e instanceof ProtectionError; export async function forkRepoAction(owner: string, name: string, _prev: FormState, formData: FormData): Promise { let destination: string; try { const { repo, viewer } = await repoForAction(owner, name, "read"); const fork = await createFork( viewer, repo, { name: String(formData.get("name") ?? ""), description: String(formData.get("description") ?? ""), defaultBranchOnly: formData.get("defaultBranchOnly") === "on", }, person(viewer.id), ); revalidatePath(`/${repo.owner}/${repo.name}`, "layout"); destination = `/${fork.owner}/${fork.name}`; } catch (e) { if (isUserError(e)) return { error: (e as Error).message }; throw e; } redirect(destination); } export async function syncForkAction(owner: string, name: string): Promise { try { const { repo, viewer } = await repoForAction(owner, name, "write"); const result = await syncFork(repo, person(viewer.id)); revalidatePath(`/${repo.owner}/${repo.name}`, "layout"); return { success: result.updated ? "Synced with the upstream repository." : "Already up to date." }; } catch (e) { if (isUserError(e)) return { error: (e as Error).message }; throw e; } }