"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<FormState> {
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<FormState> {
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;
}
}