Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh
51 linesCodeBlameRaw
1import { redirect } from "react-router";
2
3import type { Route } from "./+types/github-install";
4import { INSTALL_COOKIE, connectStep, cookie, installCookieValue, newState, notYetAdded } from "../lib/github";
5import { githubApp } from "../lib/github.server";
6import { requireUser, roleIn } from "../lib/session.server";
7
8/**
9 * "Connect GitHub" for a workspace owner, in one click: links their GitHub
10 * account on the way if it is not linked yet, adds an installation they
11 * already have, and otherwise sends them to GitHub to install g1t's GitHub
12 * App. GitHub returns to /integrations/github/setup with the state, which
13 * must match the cookie set here. `?another=1` always installs, for adding
14 * one more account.
15 */
16export async function loader({ request, context }: Route.LoaderArgs) {
17 const user = requireUser(context, request);
18 const url = new URL(request.url);
19 const workspace = (url.searchParams.get("workspace") ?? "").toLowerCase();
20 const back = `/new/github?workspace=${encodeURIComponent(workspace)}`;
21 if (roleIn(user, workspace) !== "owner") throw redirect(back);
22 const status = await githubApp.status(user, workspace);
23 if (!status.ok || !status.value.installUrl) throw redirect("/new");
24 const another = url.searchParams.get("another") === "1";
25 const linked = status.value.linked;
26 const visible = linked ? await githubApp.visibleInstallations(user).catch(() => null) : null;
27 const recorded = new Set(status.value.installations.map((item) => item.id));
28 const unadded = visible?.ok ? notYetAdded(visible.value, workspace).filter((item) => !recorded.has(item.id)) : [];
29 const step = connectStep({ linked, justLinked: url.searchParams.get("linked") === "1", another, unadded });
30 switch (step.kind) {
31 case "link": {
32 // Back here once GitHub says who they are, to carry on installing.
33 const here = `${url.pathname}?workspace=${encodeURIComponent(workspace)}${another ? "&another=1" : ""}&linked=1`;
34 throw redirect(`/auth/github?link=1&next=${encodeURIComponent(here)}`);
35 }
36 case "add": {
37 const added = await githubApp.addInstallation(user, workspace, step.installation);
38 throw redirect(added.ok ? `${back}&installation=${step.installation}` : back);
39 }
40 case "choose":
41 throw redirect(back);
42 case "install": {
43 const state = newState();
44 const target = new URL(status.value.installUrl);
45 target.searchParams.set("state", state);
46 throw redirect(target.toString(), {
47 headers: { "set-cookie": cookie(INSTALL_COOKIE, installCookieValue(state, workspace), 1800) },
48 });
49 }
50 }
51}